applications: Add 3GPP HTTP model
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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*
|
||||
|
||||
173
src/applications/examples/three-gpp-http-example.cc
Normal file
173
src/applications/examples/three-gpp-http-example.cc
Normal 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;
|
||||
}
|
||||
8
src/applications/examples/wscript
Normal file
8
src/applications/examples/wscript
Normal 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'
|
||||
133
src/applications/helper/three-gpp-http-helper.cc
Normal file
133
src/applications/helper/three-gpp-http-helper.cc
Normal 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`
|
||||
172
src/applications/helper/three-gpp-http-helper.h
Normal file
172
src/applications/helper/three-gpp-http-helper.h
Normal 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 */
|
||||
878
src/applications/model/three-gpp-http-client.cc
Normal file
878
src/applications/model/three-gpp-http-client.cc
Normal 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`
|
||||
|
||||
426
src/applications/model/three-gpp-http-client.h
Normal file
426
src/applications/model/three-gpp-http-client.h
Normal 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 */
|
||||
218
src/applications/model/three-gpp-http-header.cc
Normal file
218
src/applications/model/three-gpp-http-header.cc
Normal 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
|
||||
141
src/applications/model/three-gpp-http-header.h
Normal file
141
src/applications/model/three-gpp-http-header.h
Normal 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 */
|
||||
945
src/applications/model/three-gpp-http-server.cc
Normal file
945
src/applications/model/three-gpp-http-server.cc
Normal 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`
|
||||
564
src/applications/model/three-gpp-http-server.h
Normal file
564
src/applications/model/three-gpp-http-server.h
Normal 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 */
|
||||
492
src/applications/model/three-gpp-http-variables.cc
Normal file
492
src/applications/model/three-gpp-http-variables.cc
Normal 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`
|
||||
339
src/applications/model/three-gpp-http-variables.h
Normal file
339
src/applications/model/three-gpp-http-variables.h
Normal 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 */
|
||||
|
||||
908
src/applications/test/three-gpp-http-client-server-test.cc
Normal file
908
src/applications/test/three-gpp-http-client-server-test.cc
Normal 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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user