applications: Add 3GPP HTTP model

This commit is contained in:
Budiarto Herman
2018-06-08 10:55:56 -07:00
parent a5cf957540
commit 372b008e5d
18 changed files with 5667 additions and 4 deletions

View File

@@ -885,6 +885,7 @@ EXAMPLE_RECURSIVE = NO
# \image command).
IMAGE_PATH = doc/ns3_html_theme/static \
src/applications/doc \
src/lte/doc/source/figures \
src/lte/test/reference \
src/mesh/doc \

View File

@@ -106,6 +106,11 @@ SOURCEFIGS = \
figures/testbed.dia \
figures/emulated-channel.dia \
$(SRC)/antenna/doc/source/figures/antenna-coordinate-system.dia \
$(SRC)/applications/doc/http-embedded-object-size.png \
$(SRC)/applications/doc/http-main-object-size.png \
$(SRC)/applications/doc/http-num-of-embedded-objects.png \
$(SRC)/applications/doc/http-parsing-time.png \
$(SRC)/applications/doc/http-reading-time.png \
$(SRC)/network/doc/packet.dia \
$(SRC)/network/doc/node.dia \
$(SRC)/network/doc/buffer.dia \

View File

@@ -1,6 +1,233 @@
.. include:: replace.txt
Applications
------------
3GPP HTTP applications
----------------------
Model Description
*****************
The model is a part of the applications library. The HTTP model is based on a commonly
used 3GPP model in standardization `[4]`_.
Design
======
This traffic generator simulates web browsing traffic using the Hypertext
Transfer Protocol (HTTP). It consists of one or more ``ThreeGppHttpClient``
applications which connect to a ``ThreeGppHttpServer`` application. The client
models a web browser which requests web pages to the server. The server
is then responsible to serve the web pages as requested. Please refer to
``ThreeGppHttpClientHelper`` and ``ThreeGppHttpServerHelper`` for usage instructions.
Technically speaking, the client transmits *request objects* to demand a
service from the server. Depending on the type of request received, the
server transmits either:
- a *main object*, i.e., the HTML file of the web page; or
- an *embedded object*, e.g., an image referenced by the HTML file.
The main and embedded object sizes are illustrated in figures :ref:`fig-http-main-object-size`
and :ref:`fig-http-embedded-object-size`.
.. _fig-http-main-object-size:
.. figure:: figures/http-main-object-size.*
:figwidth: 15cm
3GPP HTTP main object size histogram
.. _fig-http-embedded-object-size:
.. figure:: figures/http-embedded-object-size.*
:figwidth: 15cm
3GPP HTTP embedded object size histogram
\
A major portion of the traffic pattern is *reading time*, which does not
generate any traffic. Because of this, one may need to simulate a good
number of clients and/or sufficiently long simulation duration in order to
generate any significant traffic in the system. Reading time is illustrated in
:ref:`fig-http-reading-time`.
.. _fig-http-reading-time:
.. figure:: figures/http-reading-time.*
:figwidth: 15cm
3GPP HTTP reading time histogram
3GPP HTTP server description
############################
3GPP HTTP server is a model application which simulates the traffic of a web server. This
application works in conjunction with ``ThreeGppHttpClient`` applications.
The application works by responding to requests. Each request is a small
packet of data which contains ``ThreeGppHttpHeader``. The value of the *content type*
field of the header determines the type of object that the client is
requesting. The possible type is either a *main object* or an *embedded object*.
The application is responsible to generate the right type of object and send
it back to the client. The size of each object to be sent is randomly
determined (see ``ThreeGppHttpVariables``). Each object may be sent as multiple packets
due to limited socket buffer space.
To assist with the transmission, the application maintains several instances
of ``ThreeGppHttpServerTxBuffer``. Each instance keeps track of the object type to be
served and the number of bytes left to be sent.
The application accepts connection request from clients. Every connection is
kept open until the client disconnects.
Maximum transmission unit (MTU) size is configurable in ``ThreeGppHttpServer`` or in
``ThreeGppHttpVariables``. By default, the low variant is 536 bytes and high variant is 1460 bytes.
The default values are set with the intention of having a TCP header (size of which is 40 bytes) added
in the packet in such way that lower layers can avoid splitting packets. The change of MTU sizes
affects all TCP sockets after the server application has started. It is mainly visible in sizes of
packets received by ``ThreeGppHttpClient`` applications.
3GPP HTTP client description
############################
3GPP HTTP client is a model application which simulates the traffic of a web browser. This
application works in conjunction with an ThreeGppHttpServer application.
In summary, the application works as follows.
1. Upon start, it opens a connection to the destination web server
(ThreeGppHttpServer).
2. After the connection is established, the application immediately requests
a *main object* from the server by sending a request packet.
3. After receiving a main object (which can take some time if it consists of
several packets), the application "parses" the main object. Parsing time
is illustrated in figure :ref:`fig-http-parsing-time`.
4. The parsing takes a short time (randomly determined) to determine the
number of *embedded objects* (also randomly determined) in the web page.
Number of embedded object is illustrated in :ref:`fig-http-num-of-embedded-objects`.
- If at least one embedded object is determined, the application requests
the first embedded object from the server. The request for the next
embedded object follows after the previous embedded object has been
completely received.
- If there is no more embedded object to request, the application enters
the *reading time*.
5. Reading time is a long delay (again, randomly determined) where the
application does not induce any network traffic, thus simulating the user
reading the downloaded web page.
6. After the reading time is finished, the process repeats to step #2.
.. _fig-http-parsing-time:
.. figure:: figures/http-parsing-time.*
:figwidth: 15cm
3GPP HTTP parsing time histogram
.. _fig-http-num-of-embedded-objects:
.. figure:: figures/http-num-of-embedded-objects.*
:figwidth: 15cm
3GPP HTTP number of embedded objects histogram
The client models HTTP *persistent connection*, i.e., HTTP 1.1, where the
connection to the server is maintained and used for transmitting and receiving
all objects.
Each request by default has a constant size of 350 bytes. A ``ThreeGppHttpHeader``
is attached to each request packet. The header contains information
such as the content type requested (either main object or embedded object)
and the timestamp when the packet is transmitted (which will be used to
compute the delay and RTT of the packet).
References
==========
Many aspects of the traffic are randomly determined by ``ThreeGppHttpVariables``.
A separate instance of this object is used by the HTTP server and client applications.
These characteristics are based on a legacy 3GPP specification. The description
can be found in the following references:
\
.. _`[1]`:
[1] 3GPP TR 25.892, "Feasibility Study for Orthogonal Frequency Division Multiplexing (OFDM) for UTRAN enhancement"
\
.. _`[2]`:
[2] IEEE 802.16m, "Evaluation Methodology Document (EMD)", IEEE 802.16m-08/004r5, July 2008.
\
.. _`[3]`:
[3] NGMN Alliance, "NGMN Radio Access Performance Evaluation Methodology", v1.0, January 2008.
\
.. _`[4]`:
[4] 3GPP2-TSGC5, "HTTP, FTP and TCP models for 1xEV-DV simulations", 2001.
\
Usage
*****
The three-gpp-http-example can be referenced to see basic usage of the HTTP applications.
In summary, using the ``ThreeGppHttpServerHelper`` and ``ThreeGppHttpClientHelper`` allow the
user to easily install ``ThreeGppHttpServer`` and ``ThreeGppHttpClient`` applications to nodes.
The helper objects can be used to configure attribute values for the client
and server objects, but not for the ``ThreeGppHttpVariables`` object. Configuration of variables
is done by modifying attributes of ``ThreeGppHttpVariables``, which should be done prior to helpers
installing applications to nodes.
The client and server provide a number of ns-3 trace sources such as
"Tx", "Rx", "RxDelay", and "StateTransition" on the server side, and a large
number on the client side ("ConnectionEstablished",
"ConnectionClosed","TxMainObjectRequest", "TxEmbeddedObjectRequest",
"RxMainObjectPacket", "RxMainObject", "RxEmbeddedObjectPacket",
"RxEmbeddedObject", "Rx", "RxDelay", "RxRtt", "StateTransition").
Building the 3GPP HTTP applications
===================================
Building the applications does not require any special steps to be taken. It suffices to enable
the applications module.
Examples
========
For an example demonstrating HTTP applications
run::
$ ./waf --run 'three-gpp-http-example'
By default, the example will print out the web page requests of the client and responses of the
server and client receiving content packets by using LOG_INFO of ``ThreeGppHttpServer`` and ``ThreeGppHttpClient``.
Tests
=====
For testing HTTP applications, three-gpp-http-client-server-test is provided. Run::
$ ./test.py -s three-gpp-http-client-server-test
The test consists of simple Internet nodes having HTTP server and client applications installed.
Multiple variant scenarios are tested: delay is 3ms, 30ms or 300ms, bit error rate 0 or 5.0*10^(-6),
MTU size 536 or 1460 bytes and either IPV4 or IPV6 is used. A simulation with each combination of
these parameters is run multiple times to verify functionality with different random variables.
Test cases themselves are rather simple: test verifies that HTTP object packet bytes sent match
total bytes received by the client, and that ``ThreeGppHttpHeader`` matches the expected packet.
*Placeholder chapter*

View File

@@ -0,0 +1,173 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2016 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Lauri Sormunen <lauri.sormunen@magister.fi>
*/
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpExample");
void
ServerConnectionEstablished (Ptr<const ThreeGppHttpServer>, Ptr<Socket>)
{
NS_LOG_INFO ("Client has established a connection to the server.");
}
void
MainObjectGenerated (uint32_t size)
{
NS_LOG_INFO ("Server generated a main object of " << size << " bytes.");
}
void
EmbeddedObjectGenerated (uint32_t size)
{
NS_LOG_INFO ("Server generated an embedded object of " << size << " bytes.");
}
void
ServerTx (Ptr<const Packet> packet)
{
NS_LOG_INFO ("Server sent a packet of " << packet->GetSize () << " bytes.");
}
void
ClientRx (Ptr<const Packet> packet, const Address &address)
{
NS_LOG_INFO ("Client received a packet of " << packet->GetSize () << " bytes from " << address);
}
void
ClientMainObjectReceived (Ptr<const ThreeGppHttpClient>, Ptr<const Packet> packet)
{
Ptr<Packet> p = packet->Copy ();
ThreeGppHttpHeader header;
p->RemoveHeader (header);
if (header.GetContentLength () == p->GetSize ()
&& header.GetContentType () == ThreeGppHttpHeader::MAIN_OBJECT)
{
NS_LOG_INFO ("Client has succesfully received a main object of "
<< p->GetSize () << " bytes.");
}
else
{
NS_LOG_INFO ("Client failed to parse a main object. ");
}
}
void
ClientEmbeddedObjectReceived (Ptr<const ThreeGppHttpClient>, Ptr<const Packet> packet)
{
Ptr<Packet> p = packet->Copy ();
ThreeGppHttpHeader header;
p->RemoveHeader (header);
if (header.GetContentLength () == p->GetSize ()
&& header.GetContentType () == ThreeGppHttpHeader::EMBEDDED_OBJECT)
{
NS_LOG_INFO ("Client has succesfully received an embedded object of "
<< p->GetSize () << " bytes.");
}
else
{
NS_LOG_INFO ("Client failed to parse an embedded object. ");
}
}
int
main (int argc, char *argv[])
{
double simTimeSec = 300;
CommandLine cmd;
cmd.AddValue ("SimulationTime", "Length of simulation in seconds.", simTimeSec);
cmd.Parse (argc, argv);
Time::SetResolution (Time::NS);
LogComponentEnableAll (LOG_PREFIX_TIME);
//LogComponentEnableAll (LOG_PREFIX_FUNC);
//LogComponentEnable ("ThreeGppHttpClient", LOG_INFO);
///LogComponentEnable ("ThreeGppHttpServer", LOG_INFO);
LogComponentEnable ("ThreeGppHttpExample", LOG_INFO);
// Setup two nodes
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
InternetStackHelper stack;
stack.Install (nodes);
Ipv4AddressHelper address;
address.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = address.Assign (devices);
Ipv4Address serverAddress = interfaces.GetAddress (1);
// Create HTTP server helper
ThreeGppHttpServerHelper serverHelper (serverAddress);
// Install HTTP server
ApplicationContainer serverApps = serverHelper.Install (nodes.Get (1));
Ptr<ThreeGppHttpServer> httpServer = serverApps.Get (0)->GetObject<ThreeGppHttpServer> ();
// Example of connecting to the trace sources
httpServer->TraceConnectWithoutContext ("ConnectionEstablished",
MakeCallback (&ServerConnectionEstablished));
httpServer->TraceConnectWithoutContext ("MainObject", MakeCallback (&MainObjectGenerated));
httpServer->TraceConnectWithoutContext ("EmbeddedObject", MakeCallback (&EmbeddedObjectGenerated));
httpServer->TraceConnectWithoutContext ("Tx", MakeCallback (&ServerTx));
// Setup HTTP variables for the server
PointerValue varPtr;
httpServer->GetAttribute ("Variables", varPtr);
Ptr<ThreeGppHttpVariables> httpVariables = varPtr.Get<ThreeGppHttpVariables> ();
httpVariables->SetMainObjectSizeMean (102400); // 100kB
httpVariables->SetMainObjectSizeStdDev (40960); // 40kB
// Create HTTP client helper
ThreeGppHttpClientHelper clientHelper (serverAddress);
// Install HTTP client
ApplicationContainer clientApps = clientHelper.Install (nodes.Get (0));
Ptr<ThreeGppHttpClient> httpClient = clientApps.Get (0)->GetObject<ThreeGppHttpClient> ();
// Example of connecting to the trace sources
httpClient->TraceConnectWithoutContext ("RxMainObject", MakeCallback (&ClientMainObjectReceived));
httpClient->TraceConnectWithoutContext ("RxEmbeddedObject", MakeCallback (&ClientEmbeddedObjectReceived));
httpClient->TraceConnectWithoutContext ("Rx", MakeCallback (&ClientRx));
// Stop browsing after 30 minutes
clientApps.Stop (Seconds (simTimeSec));
Simulator::Run ();
Simulator::Destroy ();
return 0;
}

View File

@@ -0,0 +1,8 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
def build(bld):
if not bld.env['ENABLE_EXAMPLES']:
return;
obj = bld.create_ns3_program('three-gpp-http-example', ['applications','point-to-point','internet','network'])
obj.source = 'three-gpp-http-example.cc'

View File

@@ -0,0 +1,133 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 INRIA
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Original work author (from packet-sink-helper.cc):
* - Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*
* Converted to 3GPP HTTP web browsing traffic models by:
* - Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include <ns3/names.h>
#include "three-gpp-http-helper.h"
namespace ns3 {
// 3GPP HTTP CLIENT HELPER /////////////////////////////////////////////////////////
ThreeGppHttpClientHelper::ThreeGppHttpClientHelper (const Address &address)
{
m_factory.SetTypeId ("ns3::ThreeGppHttpClient");
m_factory.Set ("RemoteServerAddress", AddressValue (address));
}
void
ThreeGppHttpClientHelper::SetAttribute (const std::string &name,
const AttributeValue &value)
{
m_factory.Set (name, value);
}
ApplicationContainer
ThreeGppHttpClientHelper::Install (Ptr<Node> node) const
{
return ApplicationContainer (InstallPriv (node));
}
ApplicationContainer
ThreeGppHttpClientHelper::Install (const std::string &nodeName) const
{
Ptr<Node> node = Names::Find<Node> (nodeName);
return ApplicationContainer (InstallPriv (node));
}
ApplicationContainer
ThreeGppHttpClientHelper::Install (NodeContainer c) const
{
ApplicationContainer apps;
for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i)
{
apps.Add (InstallPriv (*i));
}
return apps;
}
Ptr<Application>
ThreeGppHttpClientHelper::InstallPriv (Ptr<Node> node) const
{
Ptr<Application> app = m_factory.Create<Application> ();
node->AddApplication (app);
return app;
}
// HTTP SERVER HELPER /////////////////////////////////////////////////////////
ThreeGppHttpServerHelper::ThreeGppHttpServerHelper (const Address &address)
{
m_factory.SetTypeId ("ns3::ThreeGppHttpServer");
m_factory.Set ("LocalAddress", AddressValue (address));
}
void
ThreeGppHttpServerHelper::SetAttribute (const std::string &name,
const AttributeValue &value)
{
m_factory.Set (name, value);
}
ApplicationContainer
ThreeGppHttpServerHelper::Install (Ptr<Node> node) const
{
return ApplicationContainer (InstallPriv (node));
}
ApplicationContainer
ThreeGppHttpServerHelper::Install (const std::string &nodeName) const
{
Ptr<Node> node = Names::Find<Node> (nodeName);
return ApplicationContainer (InstallPriv (node));
}
ApplicationContainer
ThreeGppHttpServerHelper::Install (NodeContainer c) const
{
ApplicationContainer apps;
for (NodeContainer::Iterator i = c.Begin (); i != c.End (); ++i)
{
apps.Add (InstallPriv (*i));
}
return apps;
}
Ptr<Application>
ThreeGppHttpServerHelper::InstallPriv (Ptr<Node> node) const
{
Ptr<Application> app = m_factory.Create<Application> ();
node->AddApplication (app);
return app;
}
} // end of `namespace ns3`

View File

@@ -0,0 +1,172 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 INRIA
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Original work author (from packet-sink-helper.cc):
* - Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*
* Converted to HTTP web browsing traffic models by:
* - Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#ifndef THREE_GPP_HTTP_HELPER_H
#define THREE_GPP_HTTP_HELPER_H
#include <ns3/object-factory.h>
#include <ns3/node-container.h>
#include <ns3/application-container.h>
namespace ns3 {
/**
* \ingroup applications
* Helper to make it easier to instantiate an ThreeGppHttpClient on a set of nodes.
*/
class ThreeGppHttpClientHelper
{
public:
/**
* Create a ThreeGppHttpClientHelper to make it easier to work with ThreeGppHttpClient
* applications.
* \param address The address of the remote server node to send traffic to.
*/
ThreeGppHttpClientHelper (const Address &address);
/**
* Helper function used to set the underlying application attributes, but
* *not* the socket attributes.
* \param name The name of the application attribute to set.
* \param value The value of the application attribute to set.
*/
void SetAttribute (const std::string &name,
const AttributeValue &value);
/**
* Install a ThreeGppHttpClient on each node of the input container configured with
* all the attributes set with SetAttribute().
* \param c NodeContainer of the set of nodes on which an ThreeGppHttpClient
* will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (NodeContainer c) const;
/**
* Install a ThreeGppHttpClient on each node of the input container
* configured with all the attributes set with SetAttribute().
* \param node The node on which an ThreeGppHttpClient will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (Ptr<Node> node) const;
/**
* Install a ThreeGppHttpClient on each node of the input container
* configured with all the attributes set with SetAttribute().
* \param nodeName The name of the node on which an ThreeGppHttpClient
* will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (const std::string &nodeName) const;
private:
/**
* \internal
* Install a ThreeGppHttpClient on the node configured with all the
* attributes set with SetAttribute().
* \param node The node on which an ThreeGppHttpClient will be installed.
* \return Ptr to the application installed.
*/
Ptr<Application> InstallPriv (Ptr<Node> node) const;
/// Used to instantiate an ThreeGppHttpClient instance.
ObjectFactory m_factory;
}; // end of `class ThreeGppHttpClientHelper`
/**
* \ingroup http
* Helper to make it easier to instantiate an ThreeGppHttpServer on a set of nodes.
*/
class ThreeGppHttpServerHelper
{
public:
/**
* Create a ThreeGppHttpServerHelper to make it easier to work with
* ThreeGppHttpServer applications.
* \param address The address of the server.
*/
ThreeGppHttpServerHelper (const Address &address);
/**
* Helper function used to set the underlying application attributes, but
* *not* the socket attributes.
* \param name The name of the application attribute to set.
* \param value The value of the application attribute to set.
*/
void SetAttribute (const std::string &name,
const AttributeValue &value);
/**
* Install an ThreeGppHttpServer on each node of the input container
* configured with all the attributes set with SetAttribute().
* \param c NodeContainer of the set of nodes on which an ThreeGppHttpServer
* will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (NodeContainer c) const;
/**
* Install an ThreeGppHttpServer on each node of the input container
* configured with all the attributes set with SetAttribute().
* \param node The node on which an ThreeGppHttpServer will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (Ptr<Node> node) const;
/**
* Install an ThreeGppHttpServer on each node of the input container
* configured with all the attributes set with SetAttribute().
* \param nodeName The name of the node on which an ThreeGppHttpServer
* will be installed.
* \return Container of Ptr to the applications installed.
*/
ApplicationContainer Install (const std::string &nodeName) const;
private:
/**
* \internal
* Install an ThreeGppHttpServer on the node configured with all the
* attributes set with SetAttribute().
* \param node The node on which an ThreeGppHttpServer will be installed.
* \return Ptr to the application installed.
*/
Ptr<Application> InstallPriv (Ptr<Node> node) const;
/// Used to instantiate a ThreeGppHttpServer instance.
ObjectFactory m_factory;
}; // end of `class ThreeGppHttpServerHelper`
} // end of `namespace ns3`
#endif /* THREE_GPP_HTTP_HELPER_H */

View File

@@ -0,0 +1,878 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include "three-gpp-http-client.h"
#include <ns3/log.h>
#include <ns3/simulator.h>
#include <ns3/callback.h>
#include <ns3/pointer.h>
#include <ns3/uinteger.h>
#include <ns3/double.h>
#include <ns3/three-gpp-http-variables.h>
#include <ns3/packet.h>
#include <ns3/socket.h>
#include <ns3/tcp-socket-factory.h>
#include <ns3/inet-socket-address.h>
#include <ns3/inet6-socket-address.h>
#include <ns3/unused.h>
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpClient");
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (ThreeGppHttpClient);
ThreeGppHttpClient::ThreeGppHttpClient ()
: m_state (NOT_STARTED),
m_socket (0),
m_objectBytesToBeReceived (0),
m_objectClientTs (MilliSeconds (0)),
m_objectServerTs (MilliSeconds (0)),
m_embeddedObjectsToBeRequested (0),
m_httpVariables (CreateObject<ThreeGppHttpVariables> ())
{
NS_LOG_FUNCTION (this);
}
// static
TypeId
ThreeGppHttpClient::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::ThreeGppHttpClient")
.SetParent<Application> ()
.AddConstructor<ThreeGppHttpClient> ()
.AddAttribute ("Variables",
"Variable collection, which is used to control e.g. timing and HTTP request size.",
PointerValue (),
MakePointerAccessor (&ThreeGppHttpClient::m_httpVariables),
MakePointerChecker<ThreeGppHttpVariables> ())
.AddAttribute ("RemoteServerAddress",
"The address of the destination server.",
AddressValue (),
MakeAddressAccessor (&ThreeGppHttpClient::m_remoteServerAddress),
MakeAddressChecker ())
.AddAttribute ("RemoteServerPort",
"The destination port of the outbound packets.",
UintegerValue (80), // the default HTTP port
MakeUintegerAccessor (&ThreeGppHttpClient::m_remoteServerPort),
MakeUintegerChecker<uint16_t> ())
.AddTraceSource ("ConnectionEstablished",
"Connection to the destination web server has been established.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_connectionEstablishedTrace),
"ns3::ThreeGppHttpClient::TracedCallback")
.AddTraceSource ("ConnectionClosed",
"Connection to the destination web server is closed.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_connectionClosedTrace),
"ns3::ThreeGppHttpClient::TracedCallback")
.AddTraceSource ("Tx",
"General trace for sending a packet of any kind.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_txTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("TxMainObjectRequest",
"Sent a request for a main object.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_txMainObjectRequestTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("TxEmbeddedObjectRequest",
"Sent a request for an embedded object.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_txEmbeddedObjectRequestTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("RxMainObjectPacket",
"A packet of main object has been received.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxMainObjectPacketTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("RxMainObject",
"Received a whole main object. Header is included.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxMainObjectTrace),
"ns3::ThreeGppHttpClient::TracedCallback")
.AddTraceSource ("RxEmbeddedObjectPacket",
"A packet of embedded object has been received.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxEmbeddedObjectPacketTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("RxEmbeddedObject",
"Received a whole embedded object. Header is included.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxEmbeddedObjectTrace),
"ns3::ThreeGppHttpClient::TracedCallback")
.AddTraceSource ("Rx",
"General trace for receiving a packet of any kind.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxTrace),
"ns3::Packet::PacketAddressTracedCallback")
.AddTraceSource ("RxDelay",
"General trace of delay for receiving a complete object.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxDelayTrace),
"ns3::Application::DelayAddressCallback")
.AddTraceSource ("RxRtt",
"General trace of round trip delay time for receiving a complete object.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_rxRttTrace),
"ns3::Application::DelayAddressCallback")
.AddTraceSource ("StateTransition",
"Trace fired upon every HTTP client state transition.",
MakeTraceSourceAccessor (&ThreeGppHttpClient::m_stateTransitionTrace),
"ns3::Application::StateTransitionCallback")
;
return tid;
}
Ptr<Socket>
ThreeGppHttpClient::GetSocket () const
{
return m_socket;
}
ThreeGppHttpClient::State_t
ThreeGppHttpClient::GetState () const
{
return m_state;
}
std::string
ThreeGppHttpClient::GetStateString () const
{
return GetStateString (m_state);
}
// static
std::string
ThreeGppHttpClient::GetStateString (ThreeGppHttpClient::State_t state)
{
switch (state)
{
case NOT_STARTED:
return "NOT_STARTED";
break;
case CONNECTING:
return "CONNECTING";
break;
case EXPECTING_MAIN_OBJECT:
return "EXPECTING_MAIN_OBJECT";
break;
case PARSING_MAIN_OBJECT:
return "PARSING_MAIN_OBJECT";
break;
case EXPECTING_EMBEDDED_OBJECT:
return "EXPECTING_EMBEDDED_OBJECT";
break;
case READING:
return "READING";
break;
case STOPPED:
return "STOPPED";
break;
default:
NS_FATAL_ERROR ("Unknown state");
return "FATAL_ERROR";
break;
}
}
void
ThreeGppHttpClient::DoDispose ()
{
NS_LOG_FUNCTION (this);
if (!Simulator::IsFinished ())
{
StopApplication ();
}
Application::DoDispose (); // Chain up.
}
void
ThreeGppHttpClient::StartApplication ()
{
NS_LOG_FUNCTION (this);
if (m_state == NOT_STARTED)
{
OpenConnection ();
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for StartApplication().");
}
}
void
ThreeGppHttpClient::StopApplication ()
{
NS_LOG_FUNCTION (this);
SwitchToState (STOPPED);
CancelAllPendingEvents ();
m_socket->Close ();
m_socket->SetConnectCallback (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
m_socket->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
}
void
ThreeGppHttpClient::ConnectionSucceededCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
if (m_state == CONNECTING)
{
NS_ASSERT_MSG (m_socket == socket, "Invalid socket.");
m_connectionEstablishedTrace (this);
socket->SetRecvCallback (MakeCallback (&ThreeGppHttpClient::ReceivedDataCallback,
this));
NS_ASSERT (m_embeddedObjectsToBeRequested == 0);
m_eventRequestMainObject = Simulator::ScheduleNow (
&ThreeGppHttpClient::RequestMainObject, this);
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ConnectionSucceeded().");
}
}
void
ThreeGppHttpClient::ConnectionFailedCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
if (m_state == CONNECTING)
{
NS_LOG_ERROR ("Client failed to connect"
<< " to remote address " << m_remoteServerAddress
<< " port " << m_remoteServerPort << ".");
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ConnectionFailed().");
}
}
void
ThreeGppHttpClient::NormalCloseCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
CancelAllPendingEvents ();
if (socket->GetErrno () != Socket::ERROR_NOTERROR)
{
NS_LOG_ERROR (this << " Connection has been terminated,"
<< " error code: " << socket->GetErrno () << ".");
}
m_socket->SetCloseCallbacks (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
m_connectionClosedTrace (this);
}
void
ThreeGppHttpClient::ErrorCloseCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
CancelAllPendingEvents ();
if (socket->GetErrno () != Socket::ERROR_NOTERROR)
{
NS_LOG_ERROR (this << " Connection has been terminated,"
<< " error code: " << socket->GetErrno () << ".");
}
m_connectionClosedTrace (this);
}
void
ThreeGppHttpClient::ReceivedDataCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet;
Address from;
while ((packet = socket->RecvFrom (from)))
{
if (packet->GetSize () == 0)
{
break; // EOF
}
#ifdef NS3_LOG_ENABLE
// Some log messages.
if (InetSocketAddress::IsMatchingType (from))
{
NS_LOG_INFO (this << " A packet of " << packet->GetSize () << " bytes"
<< " received from " << InetSocketAddress::ConvertFrom (from).GetIpv4 ()
<< " port " << InetSocketAddress::ConvertFrom (from).GetPort ()
<< " / " << InetSocketAddress::ConvertFrom (from) << ".");
}
else if (Inet6SocketAddress::IsMatchingType (from))
{
NS_LOG_INFO (this << " A packet of " << packet->GetSize () << " bytes"
<< " received from " << Inet6SocketAddress::ConvertFrom (from).GetIpv6 ()
<< " port " << Inet6SocketAddress::ConvertFrom (from).GetPort ()
<< " / " << Inet6SocketAddress::ConvertFrom (from) << ".");
}
#endif /* NS3_LOG_ENABLE */
m_rxTrace (packet, from);
switch (m_state)
{
case EXPECTING_MAIN_OBJECT:
ReceiveMainObject (packet, from);
break;
case EXPECTING_EMBEDDED_OBJECT:
ReceiveEmbeddedObject (packet, from);
break;
default:
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ReceivedData().");
break;
}
} // end of `while ((packet = socket->RecvFrom (from)))`
} // end of `void ReceivedDataCallback (Ptr<Socket> socket)`
void
ThreeGppHttpClient::OpenConnection ()
{
NS_LOG_FUNCTION (this);
if (m_state == NOT_STARTED || m_state == EXPECTING_EMBEDDED_OBJECT
|| m_state == PARSING_MAIN_OBJECT || m_state == READING)
{
m_socket = Socket::CreateSocket (GetNode (),
TcpSocketFactory::GetTypeId ());
int ret;
if (Ipv4Address::IsMatchingType (m_remoteServerAddress))
{
ret = m_socket->Bind ();
NS_LOG_DEBUG (this << " Bind() return value= " << ret
<< " GetErrNo= " << m_socket->GetErrno () << ".");
Ipv4Address ipv4 = Ipv4Address::ConvertFrom (m_remoteServerAddress);
InetSocketAddress inetSocket = InetSocketAddress (ipv4,
m_remoteServerPort);
NS_LOG_INFO (this << " Connecting to " << ipv4
<< " port " << m_remoteServerPort
<< " / " << inetSocket << ".");
ret = m_socket->Connect (inetSocket);
NS_LOG_DEBUG (this << " Connect() return value= " << ret
<< " GetErrNo= " << m_socket->GetErrno () << ".");
}
else if (Ipv6Address::IsMatchingType (m_remoteServerAddress))
{
ret = m_socket->Bind6 ();
NS_LOG_DEBUG (this << " Bind6() return value= " << ret
<< " GetErrNo= " << m_socket->GetErrno () << ".");
Ipv6Address ipv6 = Ipv6Address::ConvertFrom (m_remoteServerAddress);
Inet6SocketAddress inet6Socket = Inet6SocketAddress (ipv6,
m_remoteServerPort);
NS_LOG_INFO (this << " connecting to " << ipv6
<< " port " << m_remoteServerPort
<< " / " << inet6Socket << ".");
ret = m_socket->Connect (inet6Socket);
NS_LOG_DEBUG (this << " Connect() return value= " << ret
<< " GetErrNo= " << m_socket->GetErrno () << ".");
}
NS_UNUSED (ret); // Mute compiler warning.
NS_ASSERT_MSG (m_socket != 0, "Failed creating socket.");
SwitchToState (CONNECTING);
m_socket->SetConnectCallback (MakeCallback (&ThreeGppHttpClient::ConnectionSucceededCallback,
this),
MakeCallback (&ThreeGppHttpClient::ConnectionFailedCallback,
this));
m_socket->SetCloseCallbacks (MakeCallback (&ThreeGppHttpClient::NormalCloseCallback,
this),
MakeCallback (&ThreeGppHttpClient::ErrorCloseCallback,
this));
m_socket->SetRecvCallback (MakeCallback (&ThreeGppHttpClient::ReceivedDataCallback,
this));
m_socket->SetAttribute ("MaxSegLifetime", DoubleValue (0.02)); // 20 ms.
} // end of `if (m_state == {NOT_STARTED, EXPECTING_EMBEDDED_OBJECT, PARSING_MAIN_OBJECT, READING})`
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for OpenConnection().");
}
} // end of `void OpenConnection ()`
void
ThreeGppHttpClient::RequestMainObject ()
{
NS_LOG_FUNCTION (this);
if (m_state == CONNECTING || m_state == READING)
{
ThreeGppHttpHeader header;
header.SetContentLength (0); // Request does not need any content length.
header.SetContentType (ThreeGppHttpHeader::MAIN_OBJECT);
header.SetClientTs (Simulator::Now ());
const uint32_t requestSize = m_httpVariables->GetRequestSize ();
Ptr<Packet> packet = Create<Packet> (requestSize);
packet->AddHeader (header);
const uint32_t packetSize = packet->GetSize ();
m_txMainObjectRequestTrace (packet);
m_txTrace (packet);
const int actualBytes = m_socket->Send (packet);
NS_LOG_DEBUG (this << " Send() packet " << packet
<< " of " << packet->GetSize () << " bytes,"
<< " return value= " << actualBytes << ".");
if (actualBytes != static_cast<int> (packetSize))
{
NS_LOG_ERROR (this << " Failed to send request for embedded object,"
<< " GetErrNo= " << m_socket->GetErrno () << ","
<< " waiting for another Tx opportunity.");
}
else
{
SwitchToState (EXPECTING_MAIN_OBJECT);
}
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for RequestMainObject().");
}
} // end of `void RequestMainObject ()`
void
ThreeGppHttpClient::RequestEmbeddedObject ()
{
NS_LOG_FUNCTION (this);
if (m_state == CONNECTING || m_state == PARSING_MAIN_OBJECT
|| m_state == EXPECTING_EMBEDDED_OBJECT)
{
if (m_embeddedObjectsToBeRequested > 0)
{
ThreeGppHttpHeader header;
header.SetContentLength (0); // Request does not need any content length.
header.SetContentType (ThreeGppHttpHeader::EMBEDDED_OBJECT);
header.SetClientTs (Simulator::Now ());
const uint32_t requestSize = m_httpVariables->GetRequestSize ();
Ptr<Packet> packet = Create<Packet> (requestSize);
packet->AddHeader (header);
const uint32_t packetSize = packet->GetSize ();
m_txEmbeddedObjectRequestTrace (packet);
m_txTrace (packet);
const int actualBytes = m_socket->Send (packet);
NS_LOG_DEBUG (this << " Send() packet " << packet
<< " of " << packet->GetSize () << " bytes,"
<< " return value= " << actualBytes << ".");
if (actualBytes != static_cast<int> (packetSize))
{
NS_LOG_ERROR (this << " Failed to send request for embedded object,"
<< " GetErrNo= " << m_socket->GetErrno () << ","
<< " waiting for another Tx opportunity.");
}
else
{
m_embeddedObjectsToBeRequested--;
SwitchToState (EXPECTING_EMBEDDED_OBJECT);
}
}
else
{
NS_LOG_WARN (this << " No embedded object to be requested.");
}
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for RequestEmbeddedObject().");
}
} // end of `void RequestEmbeddedObject ()`
void
ThreeGppHttpClient::ReceiveMainObject (Ptr<Packet> packet, const Address &from)
{
NS_LOG_FUNCTION (this << packet << from);
if (m_state == EXPECTING_MAIN_OBJECT)
{
/*
* In the following call to Receive(), #m_objectBytesToBeReceived *will*
* be updated. #m_objectClientTs and #m_objectServerTs *may* be updated.
* ThreeGppHttpHeader will be removed from the packet, if it is the first
* packet of the object to be received; the header will be available in
* #m_constructedPacketHeader.
* #m_constructedPacket will also be updated.
*/
Receive (packet);
m_rxMainObjectPacketTrace (packet);
if (m_objectBytesToBeReceived > 0)
{
/*
* There are more packets of this main object, so just stay still
* and wait until they arrive.
*/
NS_LOG_INFO (this << " " << m_objectBytesToBeReceived << " byte(s)"
<< " remains from this chunk of main object.");
}
else
{
/*
* This is the last packet of this main object. Acknowledge the
* reception of a whole main object
*/
NS_LOG_INFO (this << " Finished receiving a main object.");
m_rxMainObjectTrace (this, m_constructedPacket);
if (!m_objectServerTs.IsZero ())
{
m_rxDelayTrace (Simulator::Now () - m_objectServerTs, from);
m_objectServerTs = MilliSeconds (0); // Reset back to zero.
}
if (!m_objectClientTs.IsZero ())
{
m_rxRttTrace (Simulator::Now () - m_objectClientTs, from);
m_objectClientTs = MilliSeconds (0); // Reset back to zero.
}
EnterParsingTime ();
} // end of else of `if (m_objectBytesToBeReceived > 0)`
} // end of `if (m_state == EXPECTING_MAIN_OBJECT)`
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ReceiveMainObject().");
}
} // end of `void ReceiveMainObject (Ptr<Packet> packet)`
void
ThreeGppHttpClient::ReceiveEmbeddedObject (Ptr<Packet> packet, const Address &from)
{
NS_LOG_FUNCTION (this << packet << from);
if (m_state == EXPECTING_EMBEDDED_OBJECT)
{
/*
* In the following call to Receive(), #m_objectBytesToBeReceived *will*
* be updated. #m_objectClientTs and #m_objectServerTs *may* be updated.
* ThreeGppHttpHeader will be removed from the packet, if it is the first
* packet of the object to be received; the header will be available in
* #m_constructedPacket, which will also be updated.
*/
Receive (packet);
m_rxEmbeddedObjectPacketTrace (packet);
if (m_objectBytesToBeReceived > 0)
{
/*
* There are more packets of this embedded object, so just stay
* still and wait until they arrive.
*/
NS_LOG_INFO (this << " " << m_objectBytesToBeReceived << " byte(s)"
<< " remains from this chunk of embedded object");
}
else
{
/*
* This is the last packet of this embedded object. Acknowledge
* the reception of a whole embedded object
*/
NS_LOG_INFO (this << " Finished receiving an embedded object.");
m_rxEmbeddedObjectTrace (this, m_constructedPacket);
if (!m_objectServerTs.IsZero ())
{
m_rxDelayTrace (Simulator::Now () - m_objectServerTs, from);
m_objectServerTs = MilliSeconds (0); // Reset back to zero.
}
if (!m_objectClientTs.IsZero ())
{
m_rxRttTrace (Simulator::Now () - m_objectClientTs, from);
m_objectClientTs = MilliSeconds (0); // Reset back to zero.
}
if (m_embeddedObjectsToBeRequested > 0)
{
NS_LOG_INFO (this << " " << m_embeddedObjectsToBeRequested
<< " more embedded object(s) to be requested.");
// Immediately request another using the existing connection.
m_eventRequestEmbeddedObject = Simulator::ScheduleNow (
&ThreeGppHttpClient::RequestEmbeddedObject, this);
}
else
{
/*
* There is no more embedded object, the web page has been
* downloaded completely. Now is the time to read it.
*/
NS_LOG_INFO (this << " Finished receiving a web page.");
EnterReadingTime ();
}
} // end of else of `if (m_objectBytesToBeReceived > 0)`
} // end of `if (m_state == EXPECTING_EMBEDDED_OBJECT)`
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ReceiveEmbeddedObject().");
}
} // end of `void ReceiveEmbeddedObject (Ptr<Packet> packet)`
void
ThreeGppHttpClient::Receive (Ptr<Packet> packet)
{
NS_LOG_FUNCTION (this << packet);
/* In a "real" HTTP message the message size is coded differently. The use of a header
* is to avoid the burden of doing a real message parser.
*/
bool firstPacket = false;
if (m_objectBytesToBeReceived == 0)
{
// This is the first packet of the object.
firstPacket = true;
// Remove the header in order to calculate remaining data to be received.
ThreeGppHttpHeader httpHeader;
packet->RemoveHeader (httpHeader);
m_objectBytesToBeReceived = httpHeader.GetContentLength ();
m_objectClientTs = httpHeader.GetClientTs ();
m_objectServerTs = httpHeader.GetServerTs ();
// Take a copy for constructed packet trace. Note that header is included.
m_constructedPacket = packet->Copy ();
m_constructedPacket->AddHeader (httpHeader);
}
uint32_t contentSize = packet->GetSize ();
/* Note that the packet does not contain header at this point.
* The content is purely raw data, which was the only intended data to be received.
*/
if (m_objectBytesToBeReceived < contentSize)
{
NS_LOG_WARN (this << " The received packet"
<< " (" << contentSize << " bytes of content)"
<< " is larger than"
<< " the content that we expected to receive"
<< " (" << m_objectBytesToBeReceived << " bytes).");
// Stop expecting any more packet of this object.
m_objectBytesToBeReceived = 0;
m_constructedPacket = NULL;
}
else
{
m_objectBytesToBeReceived -= contentSize;
if (!firstPacket)
{
Ptr<Packet> packetCopy = packet->Copy ();
m_constructedPacket->AddAtEnd (packetCopy);
}
}
} // end of `void Receive (packet)`
void
ThreeGppHttpClient::EnterParsingTime ()
{
NS_LOG_FUNCTION (this);
if (m_state == EXPECTING_MAIN_OBJECT)
{
const Time parsingTime = m_httpVariables->GetParsingTime ();
NS_LOG_INFO (this << " The parsing of this main object"
<< " will complete in "
<< parsingTime.GetSeconds () << " seconds.");
m_eventParseMainObject = Simulator::Schedule (
parsingTime, &ThreeGppHttpClient::ParseMainObject, this);
SwitchToState (PARSING_MAIN_OBJECT);
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for EnterParsingTime().");
}
}
void
ThreeGppHttpClient::ParseMainObject ()
{
NS_LOG_FUNCTION (this);
if (m_state == PARSING_MAIN_OBJECT)
{
m_embeddedObjectsToBeRequested = m_httpVariables->GetNumOfEmbeddedObjects ();
NS_LOG_INFO (this << " Parsing has determined "
<< m_embeddedObjectsToBeRequested
<< " embedded object(s) in the main object.");
if (m_embeddedObjectsToBeRequested > 0)
{
/*
* Immediately request the first embedded object using the
* existing connection.
*/
m_eventRequestEmbeddedObject = Simulator::ScheduleNow (
&ThreeGppHttpClient::RequestEmbeddedObject, this);
}
else
{
/*
* There is no embedded object in the main object. So sit back and
* enjoy the plain web page.
*/
NS_LOG_INFO (this << " Finished receiving a web page.");
EnterReadingTime ();
}
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for ParseMainObject().");
}
} // end of `void ParseMainObject ()`
void
ThreeGppHttpClient::EnterReadingTime ()
{
NS_LOG_FUNCTION (this);
if (m_state == EXPECTING_EMBEDDED_OBJECT || m_state == PARSING_MAIN_OBJECT)
{
const Time readingTime = m_httpVariables->GetReadingTime ();
NS_LOG_INFO (this << " Client will finish reading this web page in "
<< readingTime.GetSeconds () << " seconds.");
// Schedule a request of another main object once the reading time expires.
m_eventRequestMainObject = Simulator::Schedule (
readingTime, &ThreeGppHttpClient::RequestMainObject, this);
SwitchToState (READING);
}
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for EnterReadingTime().");
}
}
void
ThreeGppHttpClient::CancelAllPendingEvents ()
{
NS_LOG_FUNCTION (this);
if (!Simulator::IsExpired (m_eventRequestMainObject))
{
NS_LOG_INFO (this << " Canceling RequestMainObject() which is due in "
<< Simulator::GetDelayLeft (m_eventRequestMainObject).GetSeconds ()
<< " seconds.");
Simulator::Cancel (m_eventRequestMainObject);
}
if (!Simulator::IsExpired (m_eventRequestEmbeddedObject))
{
NS_LOG_INFO (this << " Canceling RequestEmbeddedObject() which is due in "
<< Simulator::GetDelayLeft (m_eventRequestEmbeddedObject).GetSeconds ()
<< " seconds.");
Simulator::Cancel (m_eventRequestEmbeddedObject);
}
if (!Simulator::IsExpired (m_eventParseMainObject))
{
NS_LOG_INFO (this << " Canceling ParseMainObject() which is due in "
<< Simulator::GetDelayLeft (m_eventParseMainObject).GetSeconds ()
<< " seconds.");
Simulator::Cancel (m_eventParseMainObject);
}
}
void
ThreeGppHttpClient::SwitchToState (ThreeGppHttpClient::State_t state)
{
const std::string oldState = GetStateString ();
const std::string newState = GetStateString (state);
NS_LOG_FUNCTION (this << oldState << newState);
if ((state == EXPECTING_MAIN_OBJECT) || (state == EXPECTING_EMBEDDED_OBJECT))
{
if (m_objectBytesToBeReceived > 0)
{
NS_FATAL_ERROR ("Cannot start a new receiving session"
<< " if the previous object"
<< " (" << m_objectBytesToBeReceived << " bytes)"
<< " is not completely received yet.");
}
}
m_state = state;
NS_LOG_INFO (this << " HttpClient " << oldState
<< " --> " << newState << ".");
m_stateTransitionTrace (oldState, newState);
}
} // end of `namespace ns3`

View File

@@ -0,0 +1,426 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#ifndef THREE_GPP_HTTP_CLIENT_H
#define THREE_GPP_HTTP_CLIENT_H
#include <ns3/application.h>
#include <ns3/address.h>
#include <ns3/traced-callback.h>
#include <ns3/three-gpp-http-header.h>
namespace ns3 {
class Socket;
class Packet;
class ThreeGppHttpVariables;
/**
* \ingroup applications
* \defgroup http ThreeGppHttpClientServer
*
* This traffic generator simulates web browsing traffic using the Hypertext
* Transfer Protocol (HTTP). It consists of one or more ThreeGppHttpClient
* applications which connect to an ThreeGppHttpServer application. The client
* models a web browser which requests web pages to the server. The server
* is then responsible to serve the web pages as requested. Please refer to
* ThreeGppHttpClientHelper and ThreeGppHttpServerHelper for usage instructions.
*
* Technically speaking, the client transmits *request objects* to demand a
* service from the server. Depending on the type of request received, the
* server transmits either:
* - a *main object*, i.e., the HTML file of the web page; or
* - an *embedded object*, e.g., an image referenced by the HTML file.
*
* A major portion of the traffic pattern is *reading time*, which does not
* generate any traffic. Because of this, one may need to simulate a good
* number of clients and/or sufficiently long simulation duration in order to
* generate any significant traffic in the system.
*
* Many aspects of the traffic are randomly determined by ThreeGppHttpVariables.
* These characteristics are based on a legacy 3GPP specification. The description
* can be found in the following references:
* - 3GPP TR 25.892, "Feasibility Study for Orthogonal Frequency Division
* Multiplexing (OFDM) for UTRAN enhancement"
* - IEEE 802.16m, "Evaluation Methodology Document (EMD)",
* IEEE 802.16m-08/004r5, July 2008.
* - NGMN Alliance, "NGMN Radio Access Performance Evaluation Methodology",
* v1.0, January 2008.
* - 3GPP2-TSGC5, "HTTP, FTP and TCP models for 1xEV-DV simulations", 2001.
*/
/**
* \ingroup http
* Model application which simulates the traffic of a web browser. This
* application works in conjunction with an ThreeGppHttpServer application.
*
* In summary, the application works as follows.
* 1. Upon start, it opens a connection to the destination web server
* (ThreeGppHttpServer).
* 2. After the connection is established, the application immediately requests
* a *main object* from the server by sending a request packet.
* 3. After receiving a main object (which can take some time if it consists of
* several packets), the application "parses" the main object.
* 4. The parsing takes a short time (randomly determined) to determine the
* number of *embedded objects* (also randomly determined) in the web page.
* - If at least one embedded object is determined, the application requests
* the first embedded object from the server. The request for the next
* embedded object follows after the previous embedded object has been
* completely received.
* - If there is no more embedded object to request, the application enters
* the *reading time*.
* 5. Reading time is a long delay (again, randomly determined) where the
* application does not induce any network traffic, thus simulating the user
* reading the downloaded web page.
* 6. After the reading time is finished, the process repeats to step #2.
*
* The client models HTTP *persistent connection*, i.e., HTTP 1.1, where the
* connection to the server is maintained and used for transmitting and receiving
* all objects.
*
* Each request by default has a constant size of 350 bytes. A ThreeGppHttpHeader
* is attached to each request packet. The header contains information
* such as the content type requested (either main object or embedded object)
* and the timestamp when the packet is transmitted (which will be used to
* compute the delay and RTT of the packet).
*/
class ThreeGppHttpClient : public Application
{
public:
/**
* Creates a new instance of HTTP client application.
*
* After creation, the application must be further configured through
* attributes. To avoid having to do this process manually, please use
* ThreeGppHttpClientHelper.
*/
ThreeGppHttpClient ();
/**
* Returns the object TypeId.
* \return The object TypeId.
*/
static TypeId GetTypeId ();
/**
* Returns a pointer to the associated socket.
* \return Pointer to the associated socket.
*/
Ptr<Socket> GetSocket () const;
/// The possible states of the application.
enum State_t
{
/// Before StartApplication() is invoked.
NOT_STARTED = 0,
/// Sent the server a connection request and waiting for the server to be accept it.
CONNECTING,
/// Sent the server a request for a main object and waiting to receive the packets.
EXPECTING_MAIN_OBJECT,
/// Parsing a main object that has just been received.
PARSING_MAIN_OBJECT,
/// Sent the server a request for an embedded object and waiting to receive the packets.
EXPECTING_EMBEDDED_OBJECT,
/// User reading a web page that has just been received.
READING,
/// After StopApplication() is invoked.
STOPPED
};
/**
* Returns the current state of the application.
* \return The current state of the application.
*/
State_t GetState () const;
/**
* Returns the current state of the application in string format.
* \return The current state of the application in string format.
*/
std::string GetStateString () const;
/**
* Returns the given state in string format.
* \param state An arbitrary state of an application.
* \return The given state equivalently expressed in string format.
*/
static std::string GetStateString (State_t state);
/**
* Common callback signature for `ConnectionEstablished`, `RxMainObject`, and
* `RxEmbeddedObject` trace sources.
* \param httpClient Pointer to this instance of ThreeGppHttpClient,
* which is where the trace originated.
*/
typedef void (*TracedCallback)(Ptr<const ThreeGppHttpClient> httpClient);
protected:
// Inherited from Object base class.
virtual void DoDispose ();
// Inherited from Application base class.
virtual void StartApplication ();
virtual void StopApplication ();
private:
// SOCKET CALLBACK METHODS
/**
* Invoked when a connection is established successfully on #m_socket. This
* triggers a request for a main object.
* \param socket Pointer to the socket where the event originates from.
*/
void ConnectionSucceededCallback (Ptr<Socket> socket);
/**
* Invoked when #m_socket cannot establish a connection with the web server.
* Simulation will stop and error will be raised.
* \param socket Pointer to the socket where the event originates from.
*/
void ConnectionFailedCallback (Ptr<Socket> socket);
/**
* Invoked when connection between #m_socket and the web sever is terminated.
* Error will be logged, but simulation continues.
* \param socket Pointer to the socket where the event originates from.
*/
void NormalCloseCallback (Ptr<Socket> socket);
/**
* Invoked when connection between #m_socket and the web sever is terminated.
* Error will be logged, but simulation continues.
* \param socket Pointer to the socket where the event originates from.
*/
void ErrorCloseCallback (Ptr<Socket> socket);
/**
* Invoked when #m_socket receives some packet data. Fires the `Rx` trace
* source and triggers ReceiveMainObject() or ReceiveEmbeddedObject().
* \param socket Pointer to the socket where the event originates from.
*/
void ReceivedDataCallback (Ptr<Socket> socket);
// CONNECTION-RELATED METHOD
/**
* Initialize #m_socket to connect to the destination web server at
* #m_remoteServerAddress and #m_remoteServerPort and set up callbacks to
* listen to its event. Invoked upon the start of the application.
*/
void OpenConnection ();
// TX-RELATED METHODS
/**
* Send a request object for a main object to the destination web server.
* The size of the request packet is randomly determined by HttpVariables and
* is assumed to be smaller than 536 bytes. Fires the `TxMainObjectRequest`
* trace source.
*
* The method is invoked after a connection is established or after a
* reading time has elapsed.
*/
void RequestMainObject ();
/**
* Send a request object for an embedded object to the destination web
* server. The size of the request packet is randomly determined by
* ThreeGppHttpVariables and is assumed to be smaller than 536 bytes. Fires the
* `TxEmbeddedObjectRequest` trace source.
*/
void RequestEmbeddedObject ();
// RX-RELATED METHODS
/**
* Receive a packet of main object from the destination web server. Fires the
* `RxMainObjectPacket` trace source.
*
* A main object may come from more than one packets. This is determined by
* comparing the content length specified in the ThreeGppHttpHeader of the packet and
* the actual packet size. #m_objectBytesToBeReceived keeps track of the
* number of bytes that has been received.
*
* If the received packet is not the last packet of the object, then the
* method simply quits, expecting it to be invoked again when the next packet
* comes.
*
* If the received packet is the last packet of the object, then the method
* fires the `RxMainObject`, `RxDelay`, and `RxRtt` trace sources. The client
* then triggers EnterParsingTime().
*
* \param packet The received packet.
* \param from Address of the sender.
*/
void ReceiveMainObject (Ptr<Packet> packet, const Address &from);
/**
* Receive a packet of embedded object from the destination web server. Fires
* the `RxEmbeddedObjectPacket` trace source.
*
* An embedded object may come from more than one packets. This is determined
* by comparing the content length specified in the TheeGppHttpHeader of the packet and
* the actual packet size. #m_objectBytesToBeReceived keeps track of the
* number of bytes that has been received.
*
* If the received packet is not the last packet of the object, then the
* method simply quits, expecting it to be invoked again when the next packet
* comes.
*
* If the received packet is the last packet of the object, then the method
* fires the `RxEmbeddedObject`, `RxDelay`, and `RxRtt` trace sources.
* Depending on the number of embedded objects remaining
* (#m_embeddedObjectsToBeRequested) the client can either trigger
* RequestEmbeddedObject() or EnterReadingTime().
*
* \param packet The received packet.
* \param from Address of the sender.
*/
void ReceiveEmbeddedObject (Ptr<Packet> packet, const Address &from);
/**
* Simulate a consumption of the received packet by subtracting the packet
* size from the internal counter at #m_objectBytesToBeReceived. Also updates
* #m_objectClientTs and #m_objectServerTs according to the ThreeGppHttpHeader
* found in the packet.
*
* This method is invoked as a sub-procedure of ReceiveMainObject() and
* ReceiveEmbeddedObject().
*
* \param packet The received packet. If it is the first packet of the object,
* then it must have a ThreeGppHttpHeader attached to it.
*/
void Receive (Ptr<Packet> packet);
// OFF-TIME-RELATED METHODS
/**
* Becomes idle for a randomly determined amount of time, and then triggers
* ParseMainObject(). The length of idle time is determined by
* TheeGppHttpVariables.
*
* The method is invoked after a complete main object has been received.
*/
void EnterParsingTime ();
/**
* Randomly determines the number of embedded objects in the main object.
* ThreeGppHttpVariables is utilized for this purpose. Then proceeds with
* RequestEmbeddedObject(). If the number of embedded objects has been
* determined as zero, then EnterReadingTime() is triggered.
*
* The method is invoked after parsing time has elapsed.
*/
void ParseMainObject ();
/**
* Becomes idle for a randomly determined amount of time, and then triggers
* RequestMainObject(). The length of idle time is determined by
* ThreeGppHttpVariables.
*
* The method is invoked after a complete web page has been received.
*/
void EnterReadingTime ();
/**
* Cancels #m_eventRequestMainObject, #m_eventRequestEmbeddedObject, and
* #m_eventParseMainObject. Invoked by StopApplication() and when connection
* has been terminated.
*/
void CancelAllPendingEvents ();
/**
* Change the state of the client. Fires the `StateTransition` trace source.
* \param state The new state.
*/
void SwitchToState (State_t state);
/// The current state of the client application. Begins with NOT_STARTED.
State_t m_state;
/// The socket for sending and receiving packets to/from the web server.
Ptr<Socket> m_socket;
/// According to the content length specified by the ThreeGppHttpHeader.
uint32_t m_objectBytesToBeReceived;
/// The packet constructed of one or more parts with ThreeGppHttpHeader.
Ptr<Packet> m_constructedPacket;
/// The client time stamp of the ThreeGppHttpHeader from the last received packet.
Time m_objectClientTs;
/// The server time stamp of the ThreeGppHttpHeader from the last received packet.
Time m_objectServerTs;
/// Determined after parsing the main object.
uint32_t m_embeddedObjectsToBeRequested;
// ATTRIBUTES
/// The `Variables` attribute.
Ptr<ThreeGppHttpVariables> m_httpVariables;
/// The `RemoteServerAddress` attribute. The address of the web server.
Address m_remoteServerAddress;
/// The `RemoteServerPort` attribute.
uint16_t m_remoteServerPort;
// TRACE SOURCES
/// The `ConnectionEstablished` trace source.
ns3::TracedCallback<Ptr<const ThreeGppHttpClient> > m_connectionEstablishedTrace;
/// The `ConnectionClosed` trace source.
ns3::TracedCallback<Ptr<const ThreeGppHttpClient> > m_connectionClosedTrace;
/// The `Tx` trace source.
ns3::TracedCallback<Ptr<const Packet> > m_txTrace;
/// The `TxMainObjectRequest` trace source.
ns3::TracedCallback<Ptr<const Packet> > m_txMainObjectRequestTrace;
/// The `TxEmbeddedObjectRequest` trace source.
ns3::TracedCallback<Ptr<const Packet> > m_txEmbeddedObjectRequestTrace;
/// The `TxMainObjectPacket` trace source.
ns3::TracedCallback<Ptr<const Packet> > m_rxMainObjectPacketTrace;
/// The `TxMainObject` trace source.
ns3::TracedCallback<Ptr<const ThreeGppHttpClient>, Ptr<const Packet> > m_rxMainObjectTrace;
/// The `TxEmbeddedObjectPacket` trace source.
ns3::TracedCallback<Ptr<const Packet> > m_rxEmbeddedObjectPacketTrace;
/// The `TxEmbeddedObject` trace source.
ns3::TracedCallback<Ptr<const ThreeGppHttpClient>, Ptr<const Packet> > m_rxEmbeddedObjectTrace;
/// The `Rx` trace source.
ns3::TracedCallback<Ptr<const Packet>, const Address &> m_rxTrace;
/// The `RxDelay` trace source.
ns3::TracedCallback<const Time &, const Address &> m_rxDelayTrace;
/// The `RxRtt` trace source.
ns3::TracedCallback<const Time &, const Address &> m_rxRttTrace;
/// The `StateTransition` trace source.
ns3::TracedCallback<const std::string &, const std::string &> m_stateTransitionTrace;
// EVENTS
/**
* An event of either RequestMainObject() or OpenConnection(), scheduled to
* trigger after a connection has been established or reading time has
* elapsed.
*/
EventId m_eventRequestMainObject;
/**
* An event of either RequestEmbeddedObject() or OpenConnection().
*/
EventId m_eventRequestEmbeddedObject;
/**
* An event of ParseMainObject(), scheduled to trigger after parsing time has
* elapsed.
*/
EventId m_eventParseMainObject;
}; // end of `class ThreeGppHttpClient`
} // end of `namespace ns3`
#endif /* THREE_GPP_HTTP_CLIENT_H */

View File

@@ -0,0 +1,218 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include <ns3/log.h>
#include <ns3/packet.h>
#include <sstream>
#include "three-gpp-http-header.h"
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpHeader");
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (ThreeGppHttpHeader);
ThreeGppHttpHeader::ThreeGppHttpHeader ()
: Header (),
m_contentType (NOT_SET),
m_contentLength (0),
m_clientTs (0),
m_serverTs (0)
{
NS_LOG_FUNCTION (this);
}
// static
TypeId
ThreeGppHttpHeader::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::ThreeGppHttpHeader")
.SetParent<Header> ()
.AddConstructor<ThreeGppHttpHeader> ()
;
return tid;
}
TypeId
ThreeGppHttpHeader::GetInstanceTypeId () const
{
return GetTypeId ();
}
uint32_t
ThreeGppHttpHeader::GetSerializedSize () const
{
return 2 + 4 + 8 + 8;
}
void
ThreeGppHttpHeader::Serialize (Buffer::Iterator start) const
{
NS_LOG_FUNCTION (this << &start);
start.WriteU16 (m_contentType);
start.WriteU32 (m_contentLength);
start.WriteU64 (m_clientTs);
start.WriteU64 (m_serverTs);
}
uint32_t
ThreeGppHttpHeader::Deserialize (Buffer::Iterator start)
{
NS_LOG_FUNCTION (this << &start);
uint32_t bytesRead = 0;
// First block of 2 bytes (content type)
m_contentType = start.ReadU16 ();
bytesRead += 2;
// Second block of 4 bytes (content length)
m_contentLength = start.ReadU32 ();
bytesRead += 4;
// Third block of 8 bytes (client time stamp)
m_clientTs = start.ReadU64 ();
bytesRead += 8;
// Fourth block of 8 bytes (server time stamp)
m_serverTs = start.ReadU64 ();
bytesRead += 8;
return bytesRead;
}
void
ThreeGppHttpHeader::Print (std::ostream &os) const
{
NS_LOG_FUNCTION (this << &os);
os << "(Content-Type: " << m_contentType
<< " Content-Length: " << m_contentLength
<< " Client TS: " << TimeStep (m_clientTs).GetSeconds ()
<< " Server TS: " << TimeStep (m_serverTs).GetSeconds () << ")";
}
std::string
ThreeGppHttpHeader::ToString () const
{
NS_LOG_FUNCTION (this);
std::ostringstream oss;
Print (oss);
return oss.str ();
}
void
ThreeGppHttpHeader::SetContentType (ThreeGppHttpHeader::ContentType_t contentType)
{
NS_LOG_FUNCTION (this << static_cast<uint16_t> (contentType));
switch (contentType)
{
case NOT_SET:
m_contentType = 0;
break;
case MAIN_OBJECT:
m_contentType = 1;
break;
case EMBEDDED_OBJECT:
m_contentType = 2;
break;
default:
NS_FATAL_ERROR ("Unknown Content-Type: " << contentType);
break;
}
}
ThreeGppHttpHeader::ContentType_t
ThreeGppHttpHeader::GetContentType () const
{
ContentType_t ret;
switch (m_contentType)
{
case 0:
ret = NOT_SET;
break;
case 1:
ret = MAIN_OBJECT;
break;
case 2:
ret = EMBEDDED_OBJECT;
break;
default:
NS_FATAL_ERROR ("Unknown Content-Type: " << m_contentType);
break;
}
return ret;
}
void
ThreeGppHttpHeader::SetContentLength (uint32_t contentLength)
{
NS_LOG_FUNCTION (this << contentLength);
m_contentLength = contentLength;
}
uint32_t
ThreeGppHttpHeader::GetContentLength () const
{
return m_contentLength;
}
void
ThreeGppHttpHeader::SetClientTs (Time clientTs)
{
NS_LOG_FUNCTION (this << clientTs.GetSeconds ());
m_clientTs = clientTs.GetTimeStep ();
}
Time
ThreeGppHttpHeader::GetClientTs () const
{
return TimeStep (m_clientTs);
}
void
ThreeGppHttpHeader::SetServerTs (Time serverTs)
{
NS_LOG_FUNCTION (this << serverTs.GetSeconds ());
m_serverTs = serverTs.GetTimeStep ();
}
Time
ThreeGppHttpHeader::GetServerTs () const
{
return TimeStep (m_serverTs);
}
} // namespace ns3

View File

@@ -0,0 +1,141 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#ifndef THREE_GPP_HTTP_HEADER_H
#define THREE_GPP_HTTP_HEADER_H
#include <ns3/header.h>
#include <ns3/nstime.h>
namespace ns3 {
class Packet;
/**
* \ingroup http
* \brief Header used by web browsing applications to transmit information about
* content type, content length and timestamps for delay statistics.
*
* The header contains the following fields (and their respective size when
* serialized):
* - content type (2 bytes);
* - content length (4 bytes);
* - client time stamp (8 bytes); and
* - server time stamp (8 bytes).
*
* The header is attached to every packet transmitted by ThreeGppHttpClient and
* ThreeGppHttpServer applications. In received, split packets, only the first packet
* of transmitted object contains the header, which helps to identify how many bytes are
* left to be received.
*
* The last 2 fields allow the applications to compute the propagation delay of
* each packet. The *client TS* field indicates the time when the request
* packet is sent by the ThreeGppHttpClient, while the *server TS* field indicates the
* time when the response packet is sent by the ThreeGppHttpServer.
*/
class ThreeGppHttpHeader : public Header
{
public:
/// Creates an empty instance .
ThreeGppHttpHeader ();
/**
* Returns the object TypeId.
* \return The object TypeId.
*/
static TypeId GetTypeId ();
// Inherited from ObjectBase base class.
virtual TypeId GetInstanceTypeId () const;
// Inherited from Header base class.
virtual uint32_t GetSerializedSize () const;
virtual void Serialize (Buffer::Iterator start) const;
virtual uint32_t Deserialize (Buffer::Iterator start);
virtual void Print (std::ostream &os) const;
/**
* \return The string representation of the header.
*/
std::string ToString () const;
/// The possible types of content (default = NOT_SET).
enum ContentType_t
{
NOT_SET, ///< Integer equivalent = 0.
MAIN_OBJECT, ///< Integer equivalent = 1.
EMBEDDED_OBJECT ///< Integer equivalent = 2.
};
/**
* \param contentType The content type.
*/
void SetContentType (ContentType_t contentType);
/**
* \return The content type.
*/
ContentType_t GetContentType () const;
/**
* \param contentLength The content length (in bytes).
*/
void SetContentLength (uint32_t contentLength);
/**
* \return The content length (in bytes).
*/
uint32_t GetContentLength () const;
/**
* \param clientTs The client time stamp.
*/
void SetClientTs (Time clientTs);
/**
* \return The client time stamp.
*/
Time GetClientTs () const;
/**
* \param serverTs The server time stamp.
*/
void SetServerTs (Time serverTs);
/**
* \return The server time stamp.
*/
Time GetServerTs () const;
private:
uint16_t m_contentType; //!<" Content type field in integer format.
uint32_t m_contentLength; //!<" Content length field (in bytes unit).
uint64_t m_clientTs; //!<" Client time stamp field (in time step unit).
uint64_t m_serverTs; //!<" Server time stamp field (in time step unit).
}; // end of `class ThreeGppHttpHeader`
} // end of `namespace ns3`
#endif /* THREE_GPP_HTTP_HEADER_H */

View File

@@ -0,0 +1,945 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include "three-gpp-http-server.h"
#include <ns3/log.h>
#include <ns3/simulator.h>
#include <ns3/callback.h>
#include <ns3/config.h>
#include <ns3/pointer.h>
#include <ns3/uinteger.h>
#include <ns3/three-gpp-http-variables.h>
#include <ns3/packet.h>
#include <ns3/socket.h>
#include <ns3/tcp-socket.h>
#include <ns3/tcp-socket-factory.h>
#include <ns3/inet-socket-address.h>
#include <ns3/inet6-socket-address.h>
#include <ns3/unused.h>
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpServer");
namespace ns3 {
// HTTP SERVER ////////////////////////////////////////////////////////////////
NS_OBJECT_ENSURE_REGISTERED (ThreeGppHttpServer);
ThreeGppHttpServer::ThreeGppHttpServer ()
: m_state (NOT_STARTED),
m_initialSocket (0),
m_txBuffer (Create<ThreeGppHttpServerTxBuffer> ()),
m_httpVariables (CreateObject<ThreeGppHttpVariables> ())
{
NS_LOG_FUNCTION (this);
m_mtuSize = m_httpVariables->GetMtuSize ();
NS_LOG_INFO (this << " MTU size for this server application is "
<< m_mtuSize << " bytes.");
}
// static
TypeId
ThreeGppHttpServer::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::ThreeGppHttpServer")
.SetParent<Application> ()
.AddConstructor<ThreeGppHttpServer> ()
.AddAttribute ("Variables",
"Variable collection, which is used to control e.g. processing and "
"object generation delays.",
PointerValue (),
MakePointerAccessor (&ThreeGppHttpServer::m_httpVariables),
MakePointerChecker<ThreeGppHttpVariables> ())
.AddAttribute ("LocalAddress",
"The local address of the server, "
"i.e., the address on which to bind the Rx socket.",
AddressValue (),
MakeAddressAccessor (&ThreeGppHttpServer::m_localAddress),
MakeAddressChecker ())
.AddAttribute ("LocalPort",
"Port on which the application listen for incoming packets.",
UintegerValue (80), // the default HTTP port
MakeUintegerAccessor (&ThreeGppHttpServer::m_localPort),
MakeUintegerChecker<uint16_t> ())
.AddAttribute ("Mtu",
"Maximum transmission unit (in bytes) of the TCP sockets "
"used in this application, excluding the compulsory 40 "
"bytes TCP header. Typical values are 1460 and 536 bytes. "
"The attribute is read-only because the value is randomly "
"determined.",
TypeId::ATTR_GET,
UintegerValue (),
MakeUintegerAccessor (&ThreeGppHttpServer::m_mtuSize),
MakeUintegerChecker<uint32_t> ())
.AddTraceSource ("ConnectionEstablished",
"Connection to a remote web client has been established.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_connectionEstablishedTrace),
"ns3::HttpServer::ConnectionEstablishedCallback")
.AddTraceSource ("MainObject",
"A main object has been generated.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_mainObjectTrace),
"ns3::HttpServer::HttpObjectCallback")
.AddTraceSource ("EmbeddedObject",
"An embedded object has been generated.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_embeddedObjectTrace),
"ns3::HttpServer::HttpObjectCallback")
.AddTraceSource ("Tx",
"A packet has been sent.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_txTrace),
"ns3::Packet::TracedCallback")
.AddTraceSource ("Rx",
"A packet has been received.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_rxTrace),
"ns3::Packet::PacketAddressTracedCallback")
.AddTraceSource ("RxDelay",
"A packet has been received with delay information.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_rxDelayTrace),
"ns3::Application::DelayAddressCallback")
.AddTraceSource ("StateTransition",
"Trace fired upon every HTTP client state transition.",
MakeTraceSourceAccessor (&ThreeGppHttpServer::m_stateTransitionTrace),
"ns3::Application::StateTransitionCallback")
;
return tid;
}
void
ThreeGppHttpServer::SetMtuSize (uint32_t mtuSize)
{
NS_LOG_FUNCTION (this << mtuSize);
m_mtuSize = mtuSize;
}
Ptr<Socket>
ThreeGppHttpServer::GetSocket () const
{
return m_initialSocket;
}
ThreeGppHttpServer::State_t
ThreeGppHttpServer::GetState () const
{
return m_state;
}
std::string
ThreeGppHttpServer::GetStateString () const
{
return GetStateString (m_state);
}
// static
std::string
ThreeGppHttpServer::GetStateString (ThreeGppHttpServer::State_t state)
{
switch (state)
{
case NOT_STARTED:
return "NOT_STARTED";
break;
case STARTED:
return "STARTED";
break;
case STOPPED:
return "STOPPED";
break;
default:
NS_FATAL_ERROR ("Unknown state");
return "FATAL_ERROR";
break;
}
}
void
ThreeGppHttpServer::DoDispose ()
{
NS_LOG_FUNCTION (this);
if (!Simulator::IsFinished ())
{
StopApplication ();
}
Application::DoDispose (); // Chain up.
}
void
ThreeGppHttpServer::StartApplication ()
{
NS_LOG_FUNCTION (this);
if (m_state == NOT_STARTED)
{
if (m_initialSocket == 0)
{
// Find the current default MTU value of TCP sockets.
Ptr<const ns3::AttributeValue> previousSocketMtu;
const TypeId tcpSocketTid = TcpSocket::GetTypeId ();
for (uint32_t i = 0; i < tcpSocketTid.GetAttributeN (); i++)
{
struct TypeId::AttributeInformation attrInfo = tcpSocketTid.GetAttribute (i);
if (attrInfo.name == "SegmentSize")
{
previousSocketMtu = attrInfo.initialValue;
}
}
// Creating a TCP socket to connect to the server.
m_initialSocket = Socket::CreateSocket (GetNode (),
TcpSocketFactory::GetTypeId ());
m_initialSocket->SetAttribute ("SegmentSize", UintegerValue (m_mtuSize));
int ret;
if (Ipv4Address::IsMatchingType (m_localAddress))
{
const Ipv4Address ipv4 = Ipv4Address::ConvertFrom (m_localAddress);
const InetSocketAddress inetSocket = InetSocketAddress (ipv4,
m_localPort);
NS_LOG_INFO (this << " Binding on " << ipv4
<< " port " << m_localPort
<< " / " << inetSocket << ".");
ret = m_initialSocket->Bind (inetSocket);
NS_LOG_DEBUG (this << " Bind() return value= " << ret
<< " GetErrNo= "
<< m_initialSocket->GetErrno () << ".");
}
else if (Ipv6Address::IsMatchingType (m_localAddress))
{
const Ipv6Address ipv6 = Ipv6Address::ConvertFrom (m_localAddress);
const Inet6SocketAddress inet6Socket = Inet6SocketAddress (ipv6,
m_localPort);
NS_LOG_INFO (this << " Binding on " << ipv6
<< " port " << m_localPort
<< " / " << inet6Socket << ".");
ret = m_initialSocket->Bind (inet6Socket);
NS_LOG_DEBUG (this << " Bind() return value= " << ret
<< " GetErrNo= "
<< m_initialSocket->GetErrno () << ".");
}
ret = m_initialSocket->Listen ();
NS_LOG_DEBUG (this << " Listen () return value= " << ret
<< " GetErrNo= " << m_initialSocket->GetErrno ()
<< ".");
NS_UNUSED (ret);
} // end of `if (m_initialSocket == 0)`
NS_ASSERT_MSG (m_initialSocket != 0, "Failed creating socket.");
m_initialSocket->SetAcceptCallback (MakeCallback (&ThreeGppHttpServer::ConnectionRequestCallback,
this),
MakeCallback (&ThreeGppHttpServer::NewConnectionCreatedCallback,
this));
m_initialSocket->SetCloseCallbacks (MakeCallback (&ThreeGppHttpServer::NormalCloseCallback,
this),
MakeCallback (&ThreeGppHttpServer::ErrorCloseCallback,
this));
m_initialSocket->SetRecvCallback (MakeCallback (&ThreeGppHttpServer::ReceivedDataCallback,
this));
m_initialSocket->SetSendCallback (MakeCallback (&ThreeGppHttpServer::SendCallback,
this));
SwitchToState (STARTED);
} // end of `if (m_state == NOT_STARTED)`
else
{
NS_FATAL_ERROR ("Invalid state " << GetStateString ()
<< " for StartApplication().");
}
} // end of `void StartApplication ()`
void
ThreeGppHttpServer::StopApplication ()
{
NS_LOG_FUNCTION (this);
SwitchToState (STOPPED);
// Close all accepted sockets.
m_txBuffer->CloseAllSockets ();
// Stop listening.
if (m_initialSocket != 0)
{
m_initialSocket->Close ();
m_initialSocket->SetAcceptCallback (MakeNullCallback<bool, Ptr<Socket>, const Address &> (),
MakeNullCallback<void, Ptr<Socket>, const Address &> ());
m_initialSocket->SetCloseCallbacks (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
m_initialSocket->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
m_initialSocket->SetSendCallback (MakeNullCallback<void, Ptr<Socket>, uint32_t > ());
}
}
bool
ThreeGppHttpServer::ConnectionRequestCallback (Ptr<Socket> socket,
const Address &address)
{
NS_LOG_FUNCTION (this << socket << address);
return true; // Unconditionally accept the connection request.
}
void
ThreeGppHttpServer::NewConnectionCreatedCallback (Ptr<Socket> socket,
const Address &address)
{
NS_LOG_FUNCTION (this << socket << address);
socket->SetCloseCallbacks (MakeCallback (&ThreeGppHttpServer::NormalCloseCallback,
this),
MakeCallback (&ThreeGppHttpServer::ErrorCloseCallback,
this));
socket->SetRecvCallback (MakeCallback (&ThreeGppHttpServer::ReceivedDataCallback,
this));
socket->SetSendCallback (MakeCallback (&ThreeGppHttpServer::SendCallback,
this));
m_connectionEstablishedTrace (this, socket);
m_txBuffer->AddSocket (socket);
/*
* A typical connection is established after receiving an empty (i.e., no
* data) TCP packet with ACK flag. The actual data will follow in a separate
* packet after that and will be received by ReceivedDataCallback().
*
* However, that empty ACK packet might get lost. In this case, we may
* receive the first data packet right here already, because it also counts
* as a new connection. The statement below attempts to fetch the data from
* that packet, if any.
*/
ReceivedDataCallback (socket);
}
void
ThreeGppHttpServer::NormalCloseCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
if (socket == m_initialSocket)
{
if (m_state == STARTED)
{
NS_FATAL_ERROR ("Initial listener socket shall not be closed"
<< " when the server instance is still running.");
}
}
else if (m_txBuffer->IsSocketAvailable (socket))
{
// The application should now prepare to close the socket.
if (m_txBuffer->IsBufferEmpty (socket))
{
/*
* Here we declare that we have nothing more to send and the socket
* may be closed immediately.
*/
socket->ShutdownSend ();
m_txBuffer->RemoveSocket (socket);
}
else
{
/*
* Remember to close the socket later, whenever the buffer becomes
* empty.
*/
m_txBuffer->PrepareClose (socket);
}
}
}
void
ThreeGppHttpServer::ErrorCloseCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
if (socket == m_initialSocket)
{
if (m_state == STARTED)
{
NS_FATAL_ERROR ("Initial listener socket shall not be closed"
<< " when the server instance is still running.");
}
}
else if (m_txBuffer->IsSocketAvailable (socket))
{
m_txBuffer->CloseSocket (socket);
}
}
void
ThreeGppHttpServer::ReceivedDataCallback (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet;
Address from;
while ((packet = socket->RecvFrom (from)))
{
if (packet->GetSize () == 0)
{
break; // EOF
}
#ifdef NS3_LOG_ENABLE
// Some log messages.
if (InetSocketAddress::IsMatchingType (from))
{
NS_LOG_INFO (this << " A packet of " << packet->GetSize () << " bytes"
<< " received from " << InetSocketAddress::ConvertFrom (from).GetIpv4 ()
<< " port " << InetSocketAddress::ConvertFrom (from).GetPort ()
<< " / " << InetSocketAddress::ConvertFrom (from));
}
else if (Inet6SocketAddress::IsMatchingType (from))
{
NS_LOG_INFO (this << " A packet of " << packet->GetSize () << " bytes"
<< " received from " << Inet6SocketAddress::ConvertFrom (from).GetIpv6 ()
<< " port " << Inet6SocketAddress::ConvertFrom (from).GetPort ()
<< " / " << Inet6SocketAddress::ConvertFrom (from));
}
#endif /* NS3_LOG_ENABLE */
// Check the header. No need to remove it, since it is not a "real" header.
ThreeGppHttpHeader httpHeader;
packet->PeekHeader (httpHeader);
// Fire trace sources.
m_rxTrace (packet, from);
m_rxDelayTrace (Simulator::Now () - httpHeader.GetClientTs (), from);
Time processingDelay;
switch (httpHeader.GetContentType ())
{
case ThreeGppHttpHeader::MAIN_OBJECT:
processingDelay = m_httpVariables->GetMainObjectGenerationDelay ();
NS_LOG_INFO (this << " Will finish generating a main object"
<< " in " << processingDelay.GetSeconds () << " seconds.");
m_txBuffer->RecordNextServe (socket,
Simulator::Schedule (processingDelay,
&ThreeGppHttpServer::ServeNewMainObject,
this, socket),
httpHeader.GetClientTs ());
break;
case ThreeGppHttpHeader::EMBEDDED_OBJECT:
processingDelay = m_httpVariables->GetEmbeddedObjectGenerationDelay ();
NS_LOG_INFO (this << " Will finish generating an embedded object"
<< " in " << processingDelay.GetSeconds () << " seconds.");
m_txBuffer->RecordNextServe (socket,
Simulator::Schedule (processingDelay,
&ThreeGppHttpServer::ServeNewEmbeddedObject,
this, socket),
httpHeader.GetClientTs ());
break;
default:
NS_FATAL_ERROR ("Invalid packet.");
break;
}
} // end of `while ((packet = socket->RecvFrom (from)))`
} // end of `void ReceivedDataCallback (Ptr<Socket> socket)`
void
ThreeGppHttpServer::SendCallback (Ptr<Socket> socket, uint32_t availableBufferSize)
{
NS_LOG_FUNCTION (this << socket << availableBufferSize);
if (!m_txBuffer->IsBufferEmpty (socket))
{
const uint32_t txBufferSize = m_txBuffer->GetBufferSize (socket);
const uint32_t actualSent = ServeFromTxBuffer (socket);
#ifdef NS3_LOG_ENABLE
// Some log messages.
if (actualSent < txBufferSize)
{
switch (m_txBuffer->GetBufferContentType (socket))
{
case ThreeGppHttpHeader::MAIN_OBJECT:
NS_LOG_INFO (this << " Transmission of main object is suspended"
<< " after " << actualSent << " bytes.");
break;
case ThreeGppHttpHeader::EMBEDDED_OBJECT:
NS_LOG_INFO (this << " Transmission of embedded object is suspended"
<< " after " << actualSent << " bytes.");
break;
default:
NS_FATAL_ERROR ("Invalid Tx buffer content type.");
break;
}
}
else
{
switch (m_txBuffer->GetBufferContentType (socket))
{
case ThreeGppHttpHeader::MAIN_OBJECT:
NS_LOG_INFO (this << " Finished sending a whole main object.");
break;
case ThreeGppHttpHeader::EMBEDDED_OBJECT:
NS_LOG_INFO (this << " Finished sending a whole embedded object.");
break;
default:
NS_FATAL_ERROR ("Invalid Tx buffer content type.");
break;
}
}
#endif /* NS3_LOG_ENABLE */
// Mute compiler warnings.
NS_UNUSED (txBufferSize);
NS_UNUSED (actualSent);
} // end of `if (m_txBuffer->IsBufferEmpty (socket))`
} // end of `void SendCallback (Ptr<Socket> socket, uint32_t availableBufferSize)`
void
ThreeGppHttpServer::ServeNewMainObject (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
const uint32_t objectSize = m_httpVariables->GetMainObjectSize ();
NS_LOG_INFO (this << " Main object to be served is "
<< objectSize << " bytes.");
m_mainObjectTrace (objectSize);
m_txBuffer->WriteNewObject (socket, ThreeGppHttpHeader::MAIN_OBJECT,
objectSize);
const uint32_t actualSent = ServeFromTxBuffer (socket);
if (actualSent < objectSize)
{
NS_LOG_INFO (this << " Transmission of main object is suspended"
<< " after " << actualSent << " bytes.");
}
else
{
NS_LOG_INFO (this << " Finished sending a whole main object.");
}
}
void
ThreeGppHttpServer::ServeNewEmbeddedObject (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
const uint32_t objectSize = m_httpVariables->GetEmbeddedObjectSize ();
NS_LOG_INFO (this << " Embedded object to be served is "
<< objectSize << " bytes.");
m_embeddedObjectTrace (objectSize);
m_txBuffer->WriteNewObject (socket, ThreeGppHttpHeader::EMBEDDED_OBJECT,
objectSize);
const uint32_t actualSent = ServeFromTxBuffer (socket);
if (actualSent < objectSize)
{
NS_LOG_INFO (this << " Transmission of embedded object is suspended"
<< " after " << actualSent << " bytes.");
}
else
{
NS_LOG_INFO (this << " Finished sending a whole embedded object.");
}
}
uint32_t
ThreeGppHttpServer::ServeFromTxBuffer (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
if (m_txBuffer->IsBufferEmpty (socket))
{
NS_LOG_LOGIC (this << " Tx buffer is empty. Not sending anything.");
return 0;
}
bool firstPartOfObject = !m_txBuffer->HasTxedPartOfObject (socket);
const uint32_t socketSize = socket->GetTxAvailable ();
NS_LOG_DEBUG (this << " Socket has " << socketSize
<< " bytes available for Tx.");
// Get the number of bytes remaining to be sent.
const uint32_t txBufferSize = m_txBuffer->GetBufferSize (socket);
// Compute the size of actual content to be sent; has to fit into the socket.
// Note that header size is NOT counted as TxBuffer content. Header size is overhead.
uint32_t contentSize = std::min (txBufferSize, socketSize - 22);
Ptr<Packet> packet = Create<Packet> (contentSize);
uint32_t packetSize = contentSize;
if (packetSize == 0)
{
NS_LOG_LOGIC (this << " Socket size leads to packet size of zero; not sending anything.");
return 0;
}
// If this is the first packet of an object, attach a header.
if (firstPartOfObject)
{
// Create header.
ThreeGppHttpHeader httpHeader;
httpHeader.SetContentLength (txBufferSize);
httpHeader.SetContentType (m_txBuffer->GetBufferContentType (socket));
// Using the client TS value as per the corresponding request packet.
httpHeader.SetClientTs (m_txBuffer->GetClientTs (socket));
httpHeader.SetServerTs (Simulator::Now ());
packet->AddHeader (httpHeader);
packetSize += httpHeader.GetSerializedSize ();
NS_LOG_INFO (this << " Created packet " << packet << " of "
<< packetSize << " bytes."
<< " The corresponding request came "
<< (Simulator::Now () - httpHeader.GetClientTs ()).GetSeconds ()
<< "s ago.");
}
else
{
NS_LOG_INFO (this << " Created packet " << packet << " of "
<< packetSize << " bytes to be appended to a previous packet.");
}
// Send.
const int actualBytes = socket->Send (packet);
NS_LOG_DEBUG (this << " Send() packet " << packet
<< " of " << packetSize << " bytes,"
<< " return value= " << actualBytes << ".");
m_txTrace (packet);
if (actualBytes == static_cast<int> (packetSize))
{
// The packet goes through successfully.
m_txBuffer->DepleteBufferSize (socket, contentSize);
NS_LOG_INFO (this << " Remaining object to be sent "
<< m_txBuffer->GetBufferSize (socket) << " bytes.");
return packetSize;
}
else
{
NS_LOG_INFO (this << " Failed to send object,"
<< " GetErrNo= " << socket->GetErrno () << ","
<< " suspending transmission"
<< " and waiting for another Tx opportunity.");
return 0;
}
} // end of `uint32_t ServeFromTxBuffer (Ptr<Socket> socket)`
void
ThreeGppHttpServer::SwitchToState (ThreeGppHttpServer::State_t state)
{
const std::string oldState = GetStateString ();
const std::string newState = GetStateString (state);
NS_LOG_FUNCTION (this << oldState << newState);
m_state = state;
NS_LOG_INFO (this << " ThreeGppHttpServer " << oldState
<< " --> " << newState << ".");
m_stateTransitionTrace (oldState, newState);
}
// HTTP SERVER TX BUFFER //////////////////////////////////////////////////////
ThreeGppHttpServerTxBuffer::ThreeGppHttpServerTxBuffer ()
{
NS_LOG_FUNCTION (this);
}
bool
ThreeGppHttpServerTxBuffer::IsSocketAvailable (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
return (it != m_txBuffer.end ());
}
void
ThreeGppHttpServerTxBuffer::AddSocket (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
NS_ASSERT_MSG (!IsSocketAvailable (socket),
this << " Cannot add socket " << socket
<< " because it has already been added before.");
TxBuffer_t txBuffer;
txBuffer.txBufferContentType = ThreeGppHttpHeader::NOT_SET;
txBuffer.txBufferSize = 0;
txBuffer.isClosing = false;
txBuffer.hasTxedPartOfObject = false;
m_txBuffer.insert (std::pair<Ptr<Socket>, TxBuffer_t> (socket, txBuffer));
}
void
ThreeGppHttpServerTxBuffer::RemoveSocket (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
if (!Simulator::IsExpired (it->second.nextServe))
{
NS_LOG_INFO (this << " Canceling a serving event which is due in "
<< Simulator::GetDelayLeft (it->second.nextServe).GetSeconds ()
<< " seconds.");
Simulator::Cancel (it->second.nextServe);
}
it->first->SetCloseCallbacks (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetSendCallback (MakeNullCallback<void, Ptr<Socket>, uint32_t > ());
m_txBuffer.erase (it);
}
void
ThreeGppHttpServerTxBuffer::CloseSocket (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
if (!Simulator::IsExpired (it->second.nextServe))
{
NS_LOG_INFO (this << " Canceling a serving event which is due in "
<< Simulator::GetDelayLeft (it->second.nextServe).GetSeconds ()
<< " seconds.");
Simulator::Cancel (it->second.nextServe);
}
if (it->second.txBufferSize > 0)
{
NS_LOG_WARN (this << " Closing a socket where "
<< it->second.txBufferSize << " bytes of transmission"
<< " is still pending in the corresponding Tx buffer.");
}
it->first->Close ();
it->first->SetCloseCallbacks (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetSendCallback (MakeNullCallback<void, Ptr<Socket>, uint32_t > ());
m_txBuffer.erase (it);
}
void
ThreeGppHttpServerTxBuffer::CloseAllSockets ()
{
NS_LOG_FUNCTION (this);
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
for (it = m_txBuffer.begin (); it != m_txBuffer.end (); ++it)
{
if (!Simulator::IsExpired (it->second.nextServe))
{
NS_LOG_INFO (this << " Canceling a serving event which is due in "
<< Simulator::GetDelayLeft (it->second.nextServe).GetSeconds ()
<< " seconds.");
Simulator::Cancel (it->second.nextServe);
}
it->first->Close ();
it->first->SetCloseCallbacks (MakeNullCallback<void, Ptr<Socket> > (),
MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
it->first->SetSendCallback (MakeNullCallback<void, Ptr<Socket>, uint32_t > ());
}
m_txBuffer.clear ();
}
bool
ThreeGppHttpServerTxBuffer::IsBufferEmpty (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
return (it->second.txBufferSize == 0);
}
Time
ThreeGppHttpServerTxBuffer::GetClientTs (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
return it->second.clientTs;
}
ThreeGppHttpHeader::ContentType_t
ThreeGppHttpServerTxBuffer::GetBufferContentType (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
return it->second.txBufferContentType;
}
uint32_t
ThreeGppHttpServerTxBuffer::GetBufferSize (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
return it->second.txBufferSize;
}
bool
ThreeGppHttpServerTxBuffer::HasTxedPartOfObject (Ptr<Socket> socket) const
{
std::map<Ptr<Socket>, TxBuffer_t>::const_iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found");
return it->second.hasTxedPartOfObject;
}
void
ThreeGppHttpServerTxBuffer::WriteNewObject (Ptr<Socket> socket,
ThreeGppHttpHeader::ContentType_t contentType,
uint32_t objectSize)
{
NS_LOG_FUNCTION (this << socket << contentType << objectSize);
NS_ASSERT_MSG (contentType != ThreeGppHttpHeader::NOT_SET,
"Unable to write an object without a proper Content-Type.");
NS_ASSERT_MSG (objectSize > 0,
"Unable to write a zero-sized object.");
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
NS_ASSERT_MSG (it->second.txBufferSize == 0,
"Cannot write to Tx buffer of socket " << socket
<< " until the previous content has been completely sent.");
it->second.txBufferContentType = contentType;
it->second.txBufferSize = objectSize;
it->second.hasTxedPartOfObject = false;
}
void
ThreeGppHttpServerTxBuffer::RecordNextServe (Ptr<Socket> socket,
const EventId &eventId,
const Time &clientTs)
{
NS_LOG_FUNCTION (this << socket << clientTs.GetSeconds ());
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
it->second.nextServe = eventId;
it->second.clientTs = clientTs;
}
void
ThreeGppHttpServerTxBuffer::DepleteBufferSize (Ptr<Socket> socket, uint32_t amount)
{
NS_LOG_FUNCTION (this << socket << amount);
NS_ASSERT_MSG (amount > 0, "Unable to consume zero bytes.");
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
NS_ASSERT_MSG (it->second.txBufferSize >= amount,
"The requested amount is larger than the current buffer size.");
it->second.txBufferSize -= amount;
it->second.hasTxedPartOfObject = true;
if (it->second.isClosing && (it->second.txBufferSize == 0))
{
/*
* The peer has earlier issued a close request and we have now waited
* until all the existing data are pushed into the socket. Now we close
* the socket explicitly.
*/
CloseSocket (socket);
}
}
void
ThreeGppHttpServerTxBuffer::PrepareClose (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
std::map<Ptr<Socket>, TxBuffer_t>::iterator it;
it = m_txBuffer.find (socket);
NS_ASSERT_MSG (it != m_txBuffer.end (),
"Socket " << socket << " cannot be found.");
it->second.isClosing = true;
}
} // end of `namespace ns3`

View File

@@ -0,0 +1,564 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#ifndef THREE_GPP_HTTP_SERVER_H
#define THREE_GPP_HTTP_SERVER_H
#include <ns3/ptr.h>
#include <ns3/simple-ref-count.h>
#include <ns3/nstime.h>
#include <ns3/event-id.h>
#include <ns3/three-gpp-http-header.h>
#include <ns3/application.h>
#include <ns3/address.h>
#include <ns3/traced-callback.h>
#include <map>
#include <ostream>
namespace ns3 {
class Socket;
class Packet;
class ThreeGppHttpVariables;
class ThreeGppHttpServerTxBuffer;
/**
* \ingroup http
* Model application which simulates the traffic of a web server. This
* application works in conjunction with ThreeGppHttpClient applications.
*
* The application works by responding to requests. Each request is a small
* packet of data which contains ThreeGppHttpHeader. The value of the *content
* type* field of the header determines the type of object that the client is
* requesting. The possible type is either a *main object* or an *embedded
* object*.
*
* The application is responsible to generate the right type of object and send
* it back to the client. The size of each object to be sent is randomly
* determined (see ThreeGppHttpVariables). Each object may be sent as multiple packets
* due to limited socket buffer space.
*
* To assist with the transmission, the application maintains several instances
* of ThreeGppHttpServerTxBuffer. Each instance keeps track of the object type to be
* served and the number of bytes left to be sent.
*
* The application accepts connection request from clients. Every connection is
* kept open until the client disconnects.
*/
class ThreeGppHttpServer : public Application
{
public:
/**
* Creates a new instance of HTTP server application.
*
* After creation, the application must be further configured through
* attributes. To avoid having to do this process manually, please use
* ThreeGppHttpServerHelper.
*
* Upon creation, the application randomly determines the MTU size that it
* will use (either 536 or 1460 bytes). The chosen size will be used while
* creating the listener socket.
*/
ThreeGppHttpServer ();
/**
* Returns the object TypeId.
* \return The object TypeId.
*/
static TypeId GetTypeId ();
/**
* Sets the maximum transmission unit (MTU) size used by the application.
*
* This overrides the MTU size which is randomly determined once the
* application is created. Values other than the standard 536 and 1460 bytes
* can be set using this method.
*
* \param mtuSize MTU size in bytes.
*/
void SetMtuSize (uint32_t mtuSize);
/**
* Returns a pointer to the listening socket.
* \return Pointer to the listening socket
*/
Ptr<Socket> GetSocket () const;
/// The possible states of the application.
enum State_t
{
NOT_STARTED = 0, ///< Before StartApplication() is invoked.
STARTED, ///< Passively listening and responding to requests.
STOPPED ///< After StopApplication() is invoked.
};
/**
* Returns the current state of the application.
* \return The current state of the application.
*/
State_t GetState () const;
/**
* Returns the current state of the application in string format.
* \return The current state of the application in string format.
*/
std::string GetStateString () const;
/**
* Returns the given state in string format.
* \param state An arbitrary state of an application.
* \return The given state equivalently expressed in string format.
*/
static std::string GetStateString (State_t state);
/**
* Common callback signature for `MainObject` and `EmbeddedObject` trace
* sources.
* \param size Size of the generated object in bytes.
*/
typedef void (*ThreeGppHttpObjectCallback)(uint32_t size);
/**
* Callback signature for `ConnectionEstablished` trace source.
* \param httpServer Pointer to this instance of ThreeGppHttpServer, which is where
* the trace originated.
* \param socket Pointer to the socket where the connection is established.
*/
typedef void (*ConnectionEstablishedCallback)(Ptr<const ThreeGppHttpServer> httpServer,
Ptr<Socket> socket);
protected:
// Inherited from Object base class
virtual void DoDispose ();
// Inherited from Application base class
virtual void StartApplication ();
virtual void StopApplication ();
private:
// SOCKET CALLBACK METHODS
/**
* Invoked when #m_initialSocket receives a connection request.
* \param socket Pointer to the socket where the event originates from.
* \param address The address of the remote client where the connection
* request comes from.
* \return Always true, to indicate to the other end that the connection
* request is accepted.
*/
bool ConnectionRequestCallback (Ptr<Socket> socket,
const Address &address);
/**
* Invoked when a new connection has been established.
* \param socket Pointer to the socket that maintains the connection to the
* remote client. This socket will be saved to the Tx buffer.
* \param address The address the connection is incoming from.
*/
void NewConnectionCreatedCallback (Ptr<Socket> socket,
const Address &address);
/**
* Invoked when a connection with a web client is terminated. The
* corresponding socket will be removed from Tx buffer.
* \param socket Pointer to the socket where the event originates from.
*/
void NormalCloseCallback (Ptr<Socket> socket);
/**
* Invoked when a connection with a web client is terminated. The
* corresponding socket will be removed from Tx buffer.
* \param socket Pointer to the socket where the event originates from.
*/
void ErrorCloseCallback (Ptr<Socket> socket);
/**
* Invoked when #m_initialSocket receives some packet data. It will check the
* packet for ThreeGppHttpHeader. It also fires the `Rx` trace source.
*
* Depending on the type of object requested, the method will trigger
* ServeMainObject() or ServeEmbeddedObject() after some delays.
*
* \param socket Pointer to the socket where the event originates from.
*/
void ReceivedDataCallback (Ptr<Socket> socket);
/**
* Invoked when more buffer space for transmission is added to a socket. The
* method will invoke ServeFromTxBuffer() to start some transmission using
* the socket.
* \param socket Pointer to the socket where the event originates from.
* \param availableBufferSize The number of bytes available in the socket's
* transmission buffer.
*/
void SendCallback (Ptr<Socket> socket, uint32_t availableBufferSize);
// TX-RELATED METHODS
/**
* Generates a new main object and push it into the Tx buffer.
*
* The size of the object is randomly determined by ThreeGppHttpVariables.
* Fires the `MainObject` trace source. It then immediately triggers
* ServeFromTxBuffer() to send the object.
*
* \param socket Pointer to the socket which is associated with the
* destination client.
*/
void ServeNewMainObject (Ptr<Socket> socket);
/**
* Generates a new embedded object and push it into the Tx buffer.
*
* The size of the object is randomly determined by ThreeGppHttpVariables.
* Fires the `EmbeddedObject` trace source. It then immediately triggers
* ServeFromTxBuffer() to send the object.
*
* \param socket Pointer to the socket which is associated with the
* destination client.
*/
void ServeNewEmbeddedObject (Ptr<Socket> socket);
/**
* Creates a packet out of a pending object in the Tx buffer send it over the
* given socket. If the socket capacity is smaller than the object size, then
* the method only convert a part of the object into a packet.
*
* ThreeGppHttpHeader will be attached in the beginning of each application
* layer packet - if a packet is split, then then the following parts will
* not have the header. The method fires the `Tx` trace source after sending
* the packet to the socket.
*
* This method is invoked when a new object is generated by
* ServeNewMainObject() or ServeNewEmbeddedObject(). It's also invoked when
* the socket informs (through SendCallback()) that more buffer space for
* transmission has become available.
*
* \param socket Pointer to the socket which is associated with the
* destination client.
* \return Size of the packet sent (in bytes).
*/
uint32_t ServeFromTxBuffer (Ptr<Socket> socket);
/**
* Change the state of the server. Fires the `StateTransition` trace source.
* \param state The new state.
*/
void SwitchToState (State_t state);
/// The current state of the client application. Begins with NOT_STARTED.
State_t m_state;
/// The listening socket, for receiving connection requests from clients.
Ptr<Socket> m_initialSocket;
/// Pointer to the transmission buffer.
Ptr<ThreeGppHttpServerTxBuffer> m_txBuffer;
// ATTRIBUTES
/// The `Variables` attribute.
Ptr<ThreeGppHttpVariables> m_httpVariables;
/// The `LocalAddress` attribute.
Address m_localAddress;
/// The `LocalPort` attribute.
uint16_t m_localPort;
/// The `Mtu` attribute.
uint32_t m_mtuSize;
// TRACE SOURCES
/// The `ConnectionEstablished` trace source.
TracedCallback<Ptr<const ThreeGppHttpServer>, Ptr<Socket> > m_connectionEstablishedTrace;
/// The `MainObject` trace source.
TracedCallback<uint32_t> m_mainObjectTrace;
/// The `EmbeddedObject` trace source.
TracedCallback<uint32_t> m_embeddedObjectTrace;
/// The `Tx` trace source.
TracedCallback<Ptr<const Packet> > m_txTrace;
/// The `Rx` trace source.
TracedCallback<Ptr<const Packet>, const Address &> m_rxTrace;
/// The `RxDelay` trace source.
TracedCallback<const Time &, const Address &> m_rxDelayTrace;
/// The `StateTransition` trace source.
TracedCallback<const std::string &, const std::string &> m_stateTransitionTrace;
}; // end of `class ThreeGppHttpServer`
/**
* \internal
* \ingroup http
* Transmission buffer used by an HTTP server instance.
*
* The class handles the sockets which face the connected HTTP clients. An
* individual buffer is allocated for each socket. The buffer indicates the
* length (in bytes) and the type of the data within, i.e., it does *not*
* contain the actual packet data.
*
* Types of data are expressed using the ThreeGppHttpHeader::ContentType_t type. Only one
* type of data can be active for one client at a time, i.e., the current
* content of a buffer has to be removed before a different type of data can
* be added.
*/
class ThreeGppHttpServerTxBuffer : public SimpleRefCount<ThreeGppHttpServerTxBuffer>
{
public:
/// Create an empty instance of transmission buffer.
ThreeGppHttpServerTxBuffer ();
// SOCKET MANAGEMENT
/**
* This method is typically used before calling other methods. For example,
* AddSocket() requires that the given socket does not exist among the stored
* buffers. On the other hand, all the other methods that accept a pointer to
* a socket as an argument require the existence of a buffer allocated to the
* given socket.
* \param socket Pointer to the socket to be found.
* \return True if the given socket is found within the buffer.
*/
bool IsSocketAvailable (Ptr<Socket> socket) const;
/**
* Add a new socket and create an empty transmission buffer for it. After the
* method is completed, IsSocketAvailable() for the same pointer of socket
* shall return true.
* \param socket Pointer to the new socket to be added (must not exist in the
* buffer).
* \warning Must be called only when IsSocketAvailable() for the given socket
* is false.
*/
void AddSocket (Ptr<Socket> socket);
/**
* Remove a socket and its associated transmission buffer, and then unset the
* socket's callbacks to prevent further interaction with the socket. If the
* socket has a pending transmission event, it will be canceled.
*
* This method is useful for discarding a socket which is already closed,
* e.g., by the HTTP client. This is due to the fact that double closing of a
* socket may introduce undefined behaviour.
*
* After the method is completed, IsSocketAvailable() for the same pointer of
* socket shall return false.
*
* \param socket Pointer to the socket to be removed.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
void RemoveSocket (Ptr<Socket> socket);
/**
* Close and remove a socket and its associated transmission buffer, and then
* unset the socket's callback to prevent further interaction with the
* socket.
*
* This method is similar with RemoveSocket(), except that the latter does
* not close the socket.
*
* After the method is completed, IsSocketAvailable() for the same pointer of
* socket shall return false.
*
* \param socket Pointer to the socket to be closed and removed.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
void CloseSocket (Ptr<Socket> socket);
/**
* Close and remove all stored sockets, hence clearing the buffer.
*/
void CloseAllSockets ();
// BUFFER MANAGEMENT
/**
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest.
* \return True if the current length of the transmission buffer is zero,
* i.e., no pending packet.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
bool IsBufferEmpty (Ptr<Socket> socket) const;
/**
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest
* \return The client time stamp that comes from the last request packet
* received by the given socket. It indicates the time the request
* packet was transmitted by the client.
*/
Time GetClientTs (Ptr<Socket> socket) const;
/**
* Returns ThreeGppHttpHeader::NOT_SET when the buffer is new and never been filled
* with any data before. Otherwise, returns either ThreeGppHttpHeader::MAIN_OBJECT
* or ThreeGppHttpHeader::EMBEDDED_OBJECT.
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest
* \return The content type of the current data inside the transmission
* buffer.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
ThreeGppHttpHeader::ContentType_t GetBufferContentType (Ptr<Socket> socket) const;
/**
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest
* \return The length (in bytes) of the current data inside the transmission
* buffer.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
uint32_t GetBufferSize (Ptr<Socket> socket) const;
/**
* \param socket pointer to the socket which is associated with the
* transmission buffer of interest
* \return true if the buffer content has been read since it is written
*
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*
* This method returns true after WriteNewObject() method is called. It
* becomes false after DepleteBufferSize() method is called.
*/
bool HasTxedPartOfObject (Ptr<Socket> socket) const;
/**
* Writes a data representing a new main object or embedded object to the
* transmission buffer.
*
* The stored data can be later consumed wholly of partially by
* DepleteBufferSize() method.
*
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest.
* \param contentType The content-type of the data to be written (must not
* equal to ThreeGppHttpHeader:NOT_SET).
* \param objectSize The length (in bytes) of the new object to be created
* (must be greater than zero).
* \warning Must be called only when both IsSocketAvailable() and
* IsBufferEmpty() for the given socket are true.
*/
void WriteNewObject (Ptr<Socket> socket,
ThreeGppHttpHeader::ContentType_t contentType,
uint32_t objectSize);
/**
* Informs about a pending transmission event associated with the socket, so
* that it would be automatically canceled in case the socket is closed.
*
* The method also indicates the time stamp given by the client. The time
* stamp will be included in every packet sent.
*
* \param socket pointer to the socket which is associated with the
* transmission buffer of interest
* \param eventId the event to be recorded, e.g., the return value of
* Simulator::Schedule function
* \param clientTs client time stamp
*
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
void RecordNextServe (Ptr<Socket> socket,
const EventId &eventId,
const Time &clientTs);
/**
* Decrements a buffer size by a given amount.
*
* The content type of the object to be consumed can be inquired beforehand
* by the GetBufferContentType() method.
*
* If the method has consumed all the remaining bytes within the buffer,
* IsBufferEmpty() for the buffer shall return true.
*
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest.
* \param amount The length (in bytes) to be consumed (must be greater than
* zero).
*
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true. In addition, the requested amount must be larger than
* the current buffer size, which can be checked by calling the
* GetBufferSize() method.
*/
void DepleteBufferSize (Ptr<Socket> socket, uint32_t amount);
/**
* Tell the buffer to close the associated socket once the buffer becomes
* empty.
* \param socket Pointer to the socket which is associated with the
* transmission buffer of interest.
* \warning Must be called only when IsSocketAvailable() for the given socket
* is true.
*/
void PrepareClose (Ptr<Socket> socket);
private:
/**
* Set of fields representing a single transmission buffer, which will be
* associated with a socket.
*/
struct TxBuffer_t
{
/**
* Pending transmission event which will be automatically canceled when the
* associated socket is closed.
*/
EventId nextServe;
/**
* The client time stamp that comes from the request packet. This value
* will be set in ThreeGppHttpHeader of every corresponding response packet sent, to
* be used by the client to compute round trip delay time (RTT).
*/
Time clientTs;
/**
* The content type of the current data inside the transmission buffer.
* Accessible using the GetBufferContentType() method.
*/
ThreeGppHttpHeader::ContentType_t txBufferContentType;
/**
* The length (in bytes) of the current data inside the transmission
* buffer. Accessible using the GetBufferSize() method.
*/
uint32_t txBufferSize;
/**
* True if the remote end has issued a request to close, which means that
* this socket will immediately closes itself once the buffer becomes
* empty.
*/
bool isClosing;
/**
* \brief True if the buffer content has been read since it is written.
* Accessible using the HasTxedPartOfObject() method.
*/
bool hasTxedPartOfObject;
};
/// Collection of accepted sockets and its individual transmission buffer.
std::map<Ptr<Socket>, TxBuffer_t> m_txBuffer;
}; // end of `class ThreeGppHttpServerTxBuffer`
} // end of `namespace ns3`
#endif /* THREE_GPP_HTTP_SERVER_H */

View File

@@ -0,0 +1,492 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include "three-gpp-http-variables.h"
#include <ns3/log.h>
#include <ns3/uinteger.h>
#include <ns3/double.h>
#include <math.h>
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpVariables");
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (ThreeGppHttpVariables);
ThreeGppHttpVariables::ThreeGppHttpVariables ()
{
NS_LOG_FUNCTION (this);
m_mtuSizeRng = CreateObject<UniformRandomVariable> ();
m_requestSizeRng = CreateObject<ConstantRandomVariable> ();
m_mainObjectGenerationDelayRng = CreateObject<ConstantRandomVariable> ();
m_mainObjectSizeRng = CreateObject<LogNormalRandomVariable> ();
m_embeddedObjectGenerationDelayRng = CreateObject<ConstantRandomVariable> ();
m_embeddedObjectSizeRng = CreateObject<LogNormalRandomVariable> ();
m_numOfEmbeddedObjectsRng = CreateObject<ParetoRandomVariable> ();
m_readingTimeRng = CreateObject<ExponentialRandomVariable> ();
m_parsingTimeRng = CreateObject<ExponentialRandomVariable> ();
}
// static
TypeId
ThreeGppHttpVariables::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::ThreeGppHttpVariables")
.SetParent<Object> ()
.AddConstructor<ThreeGppHttpVariables> ()
// REQUEST SIZE
.AddAttribute ("RequestSize",
"The constant size of HTTP request packet (in bytes).",
UintegerValue (328),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetRequestSize),
MakeUintegerChecker<uint32_t> ())
// MAIN OBJECT GENERATION DELAY
.AddAttribute ("MainObjectGenerationDelay",
"The constant time needed by HTTP server "
"to generate a main object as a response.",
TimeValue (MilliSeconds (0)),
MakeTimeAccessor (&ThreeGppHttpVariables::SetMainObjectGenerationDelay),
MakeTimeChecker ())
// MAIN OBJECT SIZE
.AddAttribute ("MainObjectSizeMean",
"The mean of main object sizes (in bytes).",
UintegerValue (10710),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetMainObjectSizeMean),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("MainObjectSizeStdDev",
"The standard deviation of main object sizes (in bytes).",
UintegerValue (25032),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetMainObjectSizeStdDev),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("MainObjectSizeMin",
"The minimum value of main object sizes (in bytes).",
UintegerValue (100),
MakeUintegerAccessor (&ThreeGppHttpVariables::m_mainObjectSizeMin),
MakeUintegerChecker<uint32_t> (22))
.AddAttribute ("MainObjectSizeMax",
"The maximum value of main object sizes (in bytes).",
UintegerValue (2000000), // 2 MB
MakeUintegerAccessor (&ThreeGppHttpVariables::m_mainObjectSizeMax),
MakeUintegerChecker<uint32_t> ())
// EMBEDDED OBJECT GENERATION DELAY
.AddAttribute ("EmbeddedObjectGenerationDelay",
"The constant time needed by HTTP server "
"to generate an embedded object as a response.",
TimeValue (MilliSeconds (0)),
MakeTimeAccessor (&ThreeGppHttpVariables::SetEmbeddedObjectGenerationDelay),
MakeTimeChecker ())
// EMBEDDED OBJECT SIZE
.AddAttribute ("EmbeddedObjectSizeMean",
"The mean of embedded object sizes (in bytes).",
UintegerValue (7758),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetEmbeddedObjectSizeMean),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("EmbeddedObjectSizeStdDev",
"The standard deviation of embedded object sizes (in bytes).",
UintegerValue (126168),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetEmbeddedObjectSizeStdDev),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("EmbeddedObjectSizeMin",
"The minimum value of embedded object sizes (in bytes).",
UintegerValue (50),
MakeUintegerAccessor (&ThreeGppHttpVariables::m_embeddedObjectSizeMin),
MakeUintegerChecker<uint32_t> (22))
.AddAttribute ("EmbeddedObjectSizeMax",
"The maximum value of embedded object sizes (in bytes).",
UintegerValue (2000000), // 2 MB
MakeUintegerAccessor (&ThreeGppHttpVariables::m_embeddedObjectSizeMax),
MakeUintegerChecker<uint32_t> ())
// NUMBER OF EMBEDDED OBJECTS PER PAGE
.AddAttribute ("NumOfEmbeddedObjectsMax",
"The upper bound parameter of Pareto distribution for "
"the number of embedded objects per web page. The actual "
"maximum value is this value subtracted by the scale parameter.",
UintegerValue (55),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetNumOfEmbeddedObjectsMax),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("NumOfEmbeddedObjectsShape",
"The shape parameter of Pareto distribution for "
"the number of embedded objects per web page.",
DoubleValue (1.1),
MakeDoubleAccessor (&ThreeGppHttpVariables::SetNumOfEmbeddedObjectsShape),
MakeDoubleChecker<double> ())
.AddAttribute ("NumOfEmbeddedObjectsScale",
"The scale parameter of Pareto distribution for "
"the number of embedded objects per web page.",
UintegerValue (2),
MakeUintegerAccessor (&ThreeGppHttpVariables::SetNumOfEmbeddedObjectsScale),
MakeUintegerChecker<uint32_t> ())
// READING TIME
.AddAttribute ("ReadingTimeMean",
"The mean of reading time.",
TimeValue (Seconds (30)),
MakeTimeAccessor (&ThreeGppHttpVariables::SetReadingTimeMean),
MakeTimeChecker ())
// PARSING TIME
.AddAttribute ("ParsingTimeMean",
"The mean of parsing time.",
TimeValue (MilliSeconds (130)),
MakeTimeAccessor (&ThreeGppHttpVariables::SetParsingTimeMean),
MakeTimeChecker ())
// MTU SIZE
.AddAttribute ("LowMtuSize",
"The lower MTU size.",
UintegerValue (536),
MakeUintegerAccessor (&ThreeGppHttpVariables::m_lowMtu),
MakeUintegerChecker<uint32_t> (0))
.AddAttribute ("HighMtuSize",
"The higher MTU size.",
UintegerValue (1460),
MakeUintegerAccessor (&ThreeGppHttpVariables::m_highMtu),
MakeUintegerChecker<uint32_t> (0))
.AddAttribute ("HighMtuProbability",
"The probability that higher MTU size is used.",
DoubleValue (0.76),
MakeDoubleAccessor (&ThreeGppHttpVariables::m_highMtuProbability),
MakeDoubleChecker<double> (0, 1))
;
return tid;
}
uint32_t
ThreeGppHttpVariables::GetMtuSize ()
{
const double r = m_mtuSizeRng->GetValue ();
NS_ASSERT (r >= 0.0);
NS_ASSERT (r < 1.0);
if (r < m_highMtuProbability)
{
return m_highMtu; // 1500 bytes if including TCP header.
}
else
{
return m_lowMtu; // 576 bytes if including TCP header.
}
}
uint32_t
ThreeGppHttpVariables::GetRequestSize ()
{
return m_requestSizeRng->GetInteger ();
}
Time
ThreeGppHttpVariables::GetMainObjectGenerationDelay ()
{
return Seconds (m_mainObjectGenerationDelayRng->GetValue ());
}
uint32_t
ThreeGppHttpVariables::GetMainObjectSize ()
{
// Validate parameters.
if (m_mainObjectSizeMax <= m_mainObjectSizeMin)
{
NS_FATAL_ERROR ("`MainObjectSizeMax` attribute "
<< " must be greater than"
<< " the `MainObjectSizeMin` attribute.");
}
/*
* Repeatedly draw one new random value until it falls in the interval
* [min, max). The previous validation ensures this process does not loop
* indefinitely.
*/
uint32_t value;
do
{
value = m_mainObjectSizeRng->GetInteger ();
}
while ((value < m_mainObjectSizeMin) || (value >= m_mainObjectSizeMax));
return value;
}
Time
ThreeGppHttpVariables::GetEmbeddedObjectGenerationDelay ()
{
return Seconds (m_embeddedObjectGenerationDelayRng->GetValue ());
}
uint32_t
ThreeGppHttpVariables::GetEmbeddedObjectSize ()
{
// Validate parameters.
if (m_embeddedObjectSizeMax <= m_embeddedObjectSizeMin)
{
NS_FATAL_ERROR ("`EmbeddedObjectSizeMax` attribute "
<< " must be greater than"
<< " the `EmbeddedObjectSizeMin` attribute.");
}
/*
* Repeatedly draw one new random value until it falls in the interval
* [min, max). The previous validation ensures this process does not loop
* indefinitely.
*/
uint32_t value;
do
{
value = m_embeddedObjectSizeRng->GetInteger ();
}
while ((value < m_embeddedObjectSizeMin) || (value >= m_embeddedObjectSizeMax));
return value;
}
uint32_t
ThreeGppHttpVariables::GetNumOfEmbeddedObjects ()
{
// Validate parameters.
const uint32_t upperBound =
static_cast<uint32_t> (m_numOfEmbeddedObjectsRng->GetBound ());
if (upperBound <= m_numOfEmbeddedObjectsScale)
{
NS_FATAL_ERROR ("`NumOfEmbeddedObjectsMax` attribute "
<< " must be greater than"
<< " the `NumOfEmbeddedObjectsScale` attribute.");
}
/*
* Repeatedly draw one new random value until it falls in the interval
* [scale, upperBound). The previous validation ensures this process does
* not loop indefinitely.
*/
uint32_t value;
do
{
value = m_numOfEmbeddedObjectsRng->GetInteger ();
}
while ((value < m_numOfEmbeddedObjectsScale) || (value >= upperBound));
/*
* Normalize the random value with the scale parameter. The returned value
* shall now be within the interval [0, (upperBound - scale)).
*/
return (value - m_numOfEmbeddedObjectsScale);
}
Time
ThreeGppHttpVariables::GetReadingTime ()
{
return Seconds (m_readingTimeRng->GetValue ());
}
Time
ThreeGppHttpVariables::GetParsingTime ()
{
return Seconds (m_parsingTimeRng->GetValue ());
}
int64_t
ThreeGppHttpVariables::AssignStreams (int64_t stream)
{
NS_LOG_FUNCTION (this << stream);
m_mtuSizeRng->SetStream (stream);
m_requestSizeRng->SetStream (stream + 1);
m_mainObjectGenerationDelayRng->SetStream (stream + 2);
m_mainObjectSizeRng->SetStream (stream + 3);
m_embeddedObjectGenerationDelayRng->SetStream (stream + 4);
m_embeddedObjectSizeRng->SetStream (stream + 5);
m_numOfEmbeddedObjectsRng->SetStream (stream + 6);
m_readingTimeRng->SetStream (stream + 7);
m_parsingTimeRng->SetStream (stream + 8);
return 9;
}
// SETTER METHODS /////////////////////////////////////////////////////////////
void
ThreeGppHttpVariables::SetRequestSize (uint32_t constant)
{
NS_LOG_FUNCTION (this << constant);
m_requestSizeRng->SetAttribute ("Constant",
DoubleValue (static_cast<double> (constant)));
}
void
ThreeGppHttpVariables::SetMainObjectGenerationDelay (Time constant)
{
NS_LOG_FUNCTION (this << constant.GetSeconds ());
m_mainObjectGenerationDelayRng->SetAttribute ("Constant",
DoubleValue (constant.GetSeconds ()));
}
void
ThreeGppHttpVariables::SetMainObjectSizeMean (uint32_t mean)
{
NS_LOG_FUNCTION (this << mean);
NS_ASSERT_MSG (mean > 0, "Mean must be greater than zero.");
m_mainObjectSizeMean = mean;
// Update Mu and Sigma.
const double a1 = std::pow (m_mainObjectSizeStdDev, 2.0);
const double a2 = std::pow (m_mainObjectSizeMean, 2.0);
const double a = std::log (1.0 + (a1 / a2));
const double mu = std::log (m_mainObjectSizeMean) - (0.5 * a);
const double sigma = std::sqrt (a);
NS_LOG_DEBUG (this << " Mu= " << mu << " Sigma= " << sigma << ".");
m_mainObjectSizeRng->SetAttribute ("Mu", DoubleValue (mu));
m_mainObjectSizeRng->SetAttribute ("Sigma", DoubleValue (sigma));
}
void
ThreeGppHttpVariables::SetMainObjectSizeStdDev (uint32_t stdDev)
{
NS_LOG_FUNCTION (this << stdDev);
m_mainObjectSizeStdDev = stdDev;
// Update Mu and Sigma. Same piece of code as in SetMainObjectSizeMean().
const double a1 = std::pow (m_mainObjectSizeStdDev, 2.0);
const double a2 = std::pow (m_mainObjectSizeMean, 2.0);
const double a = std::log (1.0 + (a1 / a2));
const double mu = std::log (m_mainObjectSizeMean) - (0.5 * a);
const double sigma = std::sqrt (a);
NS_LOG_DEBUG (this << " Mu= " << mu << " Sigma= " << sigma << ".");
m_mainObjectSizeRng->SetAttribute ("Mu", DoubleValue (mu));
m_mainObjectSizeRng->SetAttribute ("Sigma", DoubleValue (sigma));
}
void
ThreeGppHttpVariables::SetEmbeddedObjectGenerationDelay (Time constant)
{
NS_LOG_FUNCTION (this << constant.GetSeconds ());
m_embeddedObjectGenerationDelayRng->SetAttribute ("Constant",
DoubleValue (constant.GetSeconds ()));
}
void
ThreeGppHttpVariables::SetEmbeddedObjectSizeMean (uint32_t mean)
{
NS_LOG_FUNCTION (this << mean);
NS_ASSERT_MSG (mean > 0, "Mean must be greater than zero.");
m_embeddedObjectSizeMean = mean;
// Update Mu and Sigma.
const double a1 = std::pow (m_embeddedObjectSizeStdDev, 2.0);
const double a2 = std::pow (m_embeddedObjectSizeMean, 2.0);
const double a = std::log (1.0 + (a1 / a2));
const double mu = std::log (m_embeddedObjectSizeMean) - (0.5 * a);
const double sigma = std::sqrt (a);
NS_LOG_DEBUG (this << " Mu= " << mu << " Sigma= " << sigma << ".");
m_embeddedObjectSizeRng->SetAttribute ("Mu", DoubleValue (mu));
m_embeddedObjectSizeRng->SetAttribute ("Sigma", DoubleValue (sigma));
}
void
ThreeGppHttpVariables::SetEmbeddedObjectSizeStdDev (uint32_t stdDev)
{
NS_LOG_FUNCTION (this << stdDev);
m_embeddedObjectSizeStdDev = stdDev;
// Update Mu and Sigma. Same piece of code as in SetEmbeddedObjectSizeMean().
const double a1 = std::pow (m_embeddedObjectSizeStdDev, 2.0);
const double a2 = std::pow (m_embeddedObjectSizeMean, 2.0);
const double a = std::log (1.0 + (a1 / a2));
const double mu = std::log (m_embeddedObjectSizeMean) - (0.5 * a);
const double sigma = std::sqrt (a);
NS_LOG_DEBUG (this << " Mu= " << mu << " Sigma= " << sigma << ".");
m_embeddedObjectSizeRng->SetAttribute ("Mu", DoubleValue (mu));
m_embeddedObjectSizeRng->SetAttribute ("Sigma", DoubleValue (sigma));
}
void
ThreeGppHttpVariables::SetNumOfEmbeddedObjectsMax (uint32_t max)
{
NS_LOG_FUNCTION (this << max);
m_numOfEmbeddedObjectsRng->SetAttribute ("Bound",
DoubleValue (static_cast<double> (max)));
}
void
ThreeGppHttpVariables::SetNumOfEmbeddedObjectsShape (double shape)
{
NS_LOG_FUNCTION (this << shape);
NS_ASSERT_MSG (std::fabs (shape - 1.0) > 0.000001,
"Shape parameter must not equal to 1.0.");
m_numOfEmbeddedObjectsRng->SetAttribute ("Shape", DoubleValue (shape));
}
void
ThreeGppHttpVariables::SetNumOfEmbeddedObjectsScale (uint32_t scale)
{
NS_LOG_FUNCTION (this << scale);
NS_ASSERT_MSG (scale > 0, "Scale parameter must be greater than zero.");
m_numOfEmbeddedObjectsScale = scale;
m_numOfEmbeddedObjectsRng->SetAttribute ("Scale", DoubleValue (scale));
}
void
ThreeGppHttpVariables::SetReadingTimeMean (Time mean)
{
NS_LOG_FUNCTION (this << mean.GetSeconds ());
m_readingTimeRng->SetAttribute ("Mean", DoubleValue (mean.GetSeconds ()));
}
void
ThreeGppHttpVariables::SetParsingTimeMean (Time mean)
{
NS_LOG_FUNCTION (this << mean.GetSeconds ());
m_parsingTimeRng->SetAttribute ("Mean", DoubleValue (mean.GetSeconds ()));
}
} // end of `namespace ns3`

View File

@@ -0,0 +1,339 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#ifndef THREE_GPP_HTTP_VARIABLES_H
#define THREE_GPP_HTTP_VARIABLES_H
#include <ns3/object.h>
#include <ns3/nstime.h>
#include <ns3/random-variable-stream.h>
namespace ns3 {
/**
* \ingroup http
* Container of various random variables to assist in generating web browsing
* traffic pattern.
*
* The available random values to be retrieved are:
* - MTU size --- 536 bytes or 1460 bytes;
* - request size --- constant 350 bytes;
* - delay in generating a main object --- 0 second;
* - main object size --- truncated LogNormal distribution with a mean of
* 10710 bytes;
* \image html http-main-object-size.png
* - delay in generating an embedded object --- 0 second;
* - embedded object size (in bytes) --- truncated LogNormal distribution
* with a mean of 7758 bytes;
* \image html http-embedded-object-size.png
* - number of embedded object per web page --- truncated Pareto distribution
* with a mean of approximately 3.95 (after truncation);
* \image html http-num-of-embedded-objects.png
* - length of reading time (in seconds) --- unbounded exponential
* distribution with a mean of 30 seconds; and
* \image html http-reading-time.png
* - length of parsing time (in seconds) --- unbounded exponential
* distribution with a mean of 0.13 seconds.
* \image html http-parsing-time.png
*
* Most parameters of the random distributions are configurable via attributes
* and methods of this class. The default values are according to the following
* references:
* - 3GPP TR 25.892, "Feasibility Study for Orthogonal Frequency Division
* Multiplexing (OFDM) for UTRAN enhancement"
* - IEEE 802.16m, "Evaluation Methodology Document (EMD)",
* IEEE 802.16m-08/004r5, July 2008.
* - NGMN Alliance, "NGMN Radio Access Performance Evaluation Methodology",
* v1.0, January 2008.
* - 3GPP2-TSGC5, "HTTP, FTP and TCP models for 1xEV-DV simulations", 2001.
*/
class ThreeGppHttpVariables : public Object
{
public:
/// Create a new instance with default configuration of random distributions.
ThreeGppHttpVariables ();
/**
* Returns the object TypeId.
* \return The object TypeId.
*/
static TypeId GetTypeId ();
/**
* Draws a random value of maximum transmission unit (MTU) size in bytes.
*
* The possible MTU sizes are 1460 bytes and 536 bytes with 76% and 24%
* chances, respectively. The selected value is typically used by the sockets
* of HTTP servers to send the response packets (both main objects and
* embedded objects) to the requesting HTTP clients.
*
* \return MTU size in bytes.
*/
uint32_t GetMtuSize ();
/**
* Returns the constant HTTP request size in bytes.
*
* By default, HTTP request size is 350 bytes, which can be modified by
* setting the `RequestSize` attribute or calling the SetRequestSize()
* method. This value applies to requests by HTTP client for main objects
* and embedded objects alike.
*
* \return Request size in bytes.
*/
uint32_t GetRequestSize ();
/**
* Returns the constant length of time needed by an HTTP server to generate
* a main object.
*
* By default, main objects are generated instantly, i.e., zero delay. This
* can be modified by setting the `MainObjectGenerationDelay` attribute or by
* calling the SetMainObjectGenerationDelay() method.
*
* \return The delay for generating a main object.
*/
Time GetMainObjectGenerationDelay ();
/**
* Draws a random main object size (in bytes) to be sent by an HTTP server.
*
* The size of main objects are determined by a truncated log-normal random
* distribution. The default distribution settings produces random integers
* with a mean of 10710 bytes and a standard deviation of 25032 bytes, and
* then truncated to fit between 100 bytes and 2 MB. These default settings
* can be modified via attributes or class methods.
*
* \return Main object size in bytes.
*/
uint32_t GetMainObjectSize ();
/**
* Returns the constant length of time needed by an HTTP server to generate
* an embedded object.
*
* By default, embedded objects are generated instantly, i.e., zero delay.
* This can be modified by setting the `EmbeddedObjectGenerationDelay`
* attribute or by calling the SetEmbeddedObjectGenerationDelay() method.
*
* \return The delay for generating an embedded object.
*/
Time GetEmbeddedObjectGenerationDelay ();
/**
* Draws a random embedded object size (in bytes) to be sent by an HTTP
* server.
*
* The size of embedded objects are determined by a truncated log-normal
* random distribution. The default distribution settings produces random
* integers with a mean of 7758 bytes and a standard deviation of 126168
* bytes, and then truncated to fit between 50 bytes and 2 MB. These default
* settings can be modified via attributes or class methods.
*
* \return Embedded object size in bytes.
*/
uint32_t GetEmbeddedObjectSize ();
/**
* Draws a random integer indicating the number of embedded objects in a
* main object.
*
* The number of embedded objects in a main object is typically discovered
* when the HTTP client is parsing the main object in question. This number
* is determined by a truncated Pareto distribution. The default distribution
* settings produces (after truncation) random integers within [0, 53)
* interval, with an actual mean of approximately 3.95.
*
* \return The number of embedded objects.
*/
uint32_t GetNumOfEmbeddedObjects ();
/**
* Draws a random length of time which is spent by a hypothetical human user
* (HTTP client) to read a web page before transitioning to another web page.
*
* Reading time is determined by an exponential distribution. The default
* distribution settings produces random values with a mean of 30 seconds
* without any maximum bound. The mean can be modified by setting the
* `ReadingTimeMean` attribute or by calling the SetReadingTimeMean() method.
*
* \return Time interval for reading a web page.
*/
Time GetReadingTime ();
/**
* Draws a random length of time which simulate the small delay caused by
* HTTP client looking for any embedded objects within the received main
* object.
*
* Parsing time is determined by an exponential distribution. The default
* distribution settings produces random values with a mean of 130 ms without
* any maximum bound. The mean can be modified by setting the
* `ParsingTimeMean` attribute or by calling the SetParsingTimeMean() method.
*
* \return time interval for parsing a main object
*/
Time GetParsingTime ();
/**
* Assign a fixed random variable stream number to the random variables used
* by this model.
*
* Different random variable stream number makes random number generators to
* produce different set of random values, thus possibly resulting to
* different simulation results. On the other hand, two identical simulations
* which use the same stream number should produce identical results (the
* repeatability property of ns-3 simulation).
*
* \param stream The first stream index to use.
* \return The number of stream indices assigned by this model.
*/
int64_t AssignStreams (int64_t stream);
// SETTER METHODS
/**
* \param constant Request size in bytes.
*/
void SetRequestSize (uint32_t constant);
/**
* \param constant The delay for generating a main object.
*/
void SetMainObjectGenerationDelay (Time constant);
/**
* \param mean The mean of main object sizes in bytes. Must be greater than
* zero.
*/
void SetMainObjectSizeMean (uint32_t mean);
/**
* \param stdDev The standard deviation of main object sizes in bytes.
*/
void SetMainObjectSizeStdDev (uint32_t stdDev);
/**
* \param constant The delay for generating an embedded object.
*/
void SetEmbeddedObjectGenerationDelay (Time constant);
/**
* \param mean The mean of embedded object sizes in bytes. Must be greater
* than zero.
*/
void SetEmbeddedObjectSizeMean (uint32_t mean);
/**
* \param stdDev The standard deviation of embedded object sizes in bytes.
*/
void SetEmbeddedObjectSizeStdDev (uint32_t stdDev);
/**
* \param max The upper bound parameter of the Pareto distribution for
* determining the number of embedded objects per web page.
*/
void SetNumOfEmbeddedObjectsMax (uint32_t max);
/**
* \param shape The shape parameter of the Pareto distribution for
* determining the number of embedded objects per web page.
*/
void SetNumOfEmbeddedObjectsShape (double shape);
/**
* \param scale The scale parameter of the Pareto distribution for
* determining the number of embedded objects per web page.
*/
void SetNumOfEmbeddedObjectsScale (uint32_t scale);
/**
* \param mean The mean length of time needed for reading a web page.
*/
void SetReadingTimeMean (Time mean);
/**
* \param mean The mean length of time needed for parsing a main object.
*/
void SetParsingTimeMean (Time mean);
private:
/**
* Random variable for determining MTU size (in bytes).
*/
Ptr<UniformRandomVariable> m_mtuSizeRng;
/**
* Random variable for determining request size (in bytes).
*/
Ptr<ConstantRandomVariable> m_requestSizeRng;
/**
* Random variable for determining the delay needed to generate a main object
* (in seconds).
*/
Ptr<ConstantRandomVariable> m_mainObjectGenerationDelayRng;
/**
* Random variable for determining main object size (in bytes).
*/
Ptr<LogNormalRandomVariable> m_mainObjectSizeRng;
/// Mean parameter for #m_mainObjectSizeRng;
uint32_t m_mainObjectSizeMean;
/// Standard deviation parameter for #m_mainObjectSizeRng;
uint32_t m_mainObjectSizeStdDev;
/// Lower bound parameter for #m_mainObjectSizeRng;
uint32_t m_mainObjectSizeMin;
/// Upper bound parameter for #m_mainObjectSizeRng;
uint32_t m_mainObjectSizeMax;
/// Lower MTU size
uint32_t m_lowMtu;
/// Higher MTU size
uint32_t m_highMtu;
/// High MTU size probability
double m_highMtuProbability;
/**
* Random variable for determining the delay needed to generate an embedded
* object (in seconds).
*/
Ptr<ConstantRandomVariable> m_embeddedObjectGenerationDelayRng;
/**
* Random variable for determining embedded object size (in bytes).
*/
Ptr<LogNormalRandomVariable> m_embeddedObjectSizeRng;
/// Mean parameter for #m_embeddedObjectSizeRng.
uint32_t m_embeddedObjectSizeMean;
/// Standard deviation parameter for #m_embeddedObjectSizeRng.
uint32_t m_embeddedObjectSizeStdDev;
/// Lower bound parameter for #m_embeddedObjectSizeRng.
uint32_t m_embeddedObjectSizeMin;
/// Upper bound parameter for #m_embeddedObjectSizeRng.
uint32_t m_embeddedObjectSizeMax;
/**
* Random variable for determining the number of embedded objects.
*/
Ptr<ParetoRandomVariable> m_numOfEmbeddedObjectsRng;
/// Scale parameter for #m_numOfEmbeddedObjectsRng.
uint32_t m_numOfEmbeddedObjectsScale;
/**
* Random variable for determining the length of reading time (in seconds).
*/
Ptr<ExponentialRandomVariable> m_readingTimeRng;
/**
* Random variable for determining the length of parsing time (in seconds).
*/
Ptr<ExponentialRandomVariable> m_parsingTimeRng;
}; // end of `class TreeGppHttpVariables`
} // end of `namespace ns3`
#endif /* THREE_GPP_HTTP_VARIABLES_H */

View File

@@ -0,0 +1,908 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Magister Solutions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Budiarto Herman <budiarto.herman@magister.fi>
*
*/
#include <ns3/test.h>
#include <ns3/log.h>
#include <ns3/config.h>
#include <ns3/ptr.h>
#include <ns3/nstime.h>
#include <ns3/integer.h>
#include <ns3/simple-channel.h>
#include <ns3/node.h>
#include <ns3/packet.h>
#include <ns3/mac48-address.h>
#include <ns3/simple-net-device.h>
#include <ns3/error-model.h>
#include <ns3/ipv4-address-helper.h>
#include <ns3/ipv6-address-helper.h>
#include <ns3/internet-stack-helper.h>
#include <ns3/tcp-l4-protocol.h>
#include <ns3/tcp-congestion-ops.h>
#include <ns3/three-gpp-http-client.h>
#include <ns3/three-gpp-http-server.h>
#include <ns3/three-gpp-http-helper.h>
#include <ns3/three-gpp-http-header.h>
#include <ns3/basic-data-calculators.h>
#include <list>
#include <sstream>
NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpClientServerTest");
using namespace ns3;
// HTTP OBJECT TEST CASE //////////////////////////////////////////////////////
/**
* \ingroup http
* 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.
virtual void DoRun ();
virtual void DoTeardown ();
/**
* \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 objectss.
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.
if (m_useIpv6)
{
Ipv6InterfaceContainer ipv6Ifs
= m_ipv6AddressHelper.Assign (NetDeviceContainer (dev));
NS_ASSERT (ipv6Ifs.GetN () == 1);
assignedAddress = ipv6Ifs.GetAddress (0, 0);
}
else
{
Ipv4InterfaceContainer ipv4Ifs
= m_ipv4AddressHelper.Assign (NetDeviceContainer (dev));
NS_ASSERT (ipv4Ifs.GetN () == 1);
assignedAddress = ipv4Ifs.GetAddress (0, 0);
}
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, 0,
"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, 0,
"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;
uint32_t rxSize;
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;
uint32_t rxSize;
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;
uint32_t rxSize;
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 ().GetSeconds () << "s");
Simulator::Schedule (Seconds (1.0), &ThreeGppHttpObjectTestCase::ProgressCallback, this);
}
void
ThreeGppHttpObjectTestCase::ClientRxDelayCallback (const Time &delay,
const Address &from)
{
NS_LOG_FUNCTION (this << delay.GetSeconds () << from);
m_delayCalculator->Update (delay.GetSeconds ());
}
void
ThreeGppHttpObjectTestCase::ClientRxRttCallback (const Time &rtt,
const Address &from)
{
NS_LOG_FUNCTION (this << rtt.GetSeconds () << 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
* 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 ("three-gpp-http-client-server-test", 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.GetMilliSeconds () << "ms";
name << " ber=" << bitErrorRate;
name << " mtu=" << mtuSize;
if (useIpv6)
{
name << " IPv6";
}
else
{
name << " IPv4";
}
// Assign higher fullness for tests with higher RngRun.
TestCase::TestDuration testDuration = TestCase::QUICK;
if (rngRun > 20)
{
testDuration = TestCase::EXTENSIVE;
}
if (rngRun > 50)
{
testDuration = TestCase::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;

View File

@@ -14,16 +14,22 @@ def build(bld):
'model/udp-echo-client.cc',
'model/udp-echo-server.cc',
'model/application-packet-probe.cc',
'model/three-gpp-http-client.cc',
'model/three-gpp-http-server.cc',
'model/three-gpp-http-header.cc',
'model/three-gpp-http-variables.cc',
'helper/bulk-send-helper.cc',
'helper/on-off-helper.cc',
'helper/packet-sink-helper.cc',
'helper/udp-client-server-helper.cc',
'helper/udp-echo-helper.cc',
'helper/three-gpp-http-helper.cc',
]
applications_test = bld.create_ns3_module_test_library('applications')
applications_test.source = [
'test/udp-client-server-test.cc',
'test/three-gpp-http-client-server-test.cc',
'test/udp-client-server-test.cc'
]
headers = bld(features='ns3header')
@@ -40,11 +46,19 @@ def build(bld):
'model/udp-echo-client.h',
'model/udp-echo-server.h',
'model/application-packet-probe.h',
'model/three-gpp-http-client.h',
'model/three-gpp-http-server.h',
'model/three-gpp-http-header.h',
'model/three-gpp-http-variables.h',
'helper/bulk-send-helper.h',
'helper/on-off-helper.h',
'helper/packet-sink-helper.h',
'helper/udp-client-server-helper.h',
'helper/udp-echo-helper.h',
'helper/three-gpp-http-helper.h'
]
if (bld.env['ENABLE_EXAMPLES']):
bld.recurse('examples')
bld.ns3_python_bindings()

View File

@@ -104,6 +104,25 @@ public:
*/
void SetNode (Ptr<Node> node);
/**
* \brief Common callback signature for packet delay and address.
*
* \param delay The packet delay.
* \param from The source socket address associated with the packet,
* indicating the packet's origin.
*/
typedef void (*DelayAddressCallback) (const Time &delay,
const Address &from);
/**
* \brief Common signature used by callbacks to application's state
* transition trace source.
* \param oldState The name of the previous state.
* \param newState The name of the current state.
*/
typedef void (*StateTransitionCallback)(const std::string &oldState,
const std::string &newState);
private:
/**
* \brief Application specific startup code