1 /* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2015 Magister Solutions
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Author: Budiarto Herman <budiarto.herman@magister.fi>
19  *
20  */
21 
22 #include <ns3/test.h>
23 #include <ns3/log.h>
24 #include <ns3/config.h>
25 #include <ns3/ptr.h>
26 #include <ns3/nstime.h>
27 #include <ns3/integer.h>
28 
29 #include <ns3/simple-channel.h>
30 #include <ns3/node.h>
31 #include <ns3/packet.h>
32 #include <ns3/mac48-address.h>
33 #include <ns3/simple-net-device.h>
34 #include <ns3/error-model.h>
35 #include <ns3/ipv4-address-helper.h>
36 #include <ns3/ipv6-address-helper.h>
37 #include <ns3/internet-stack-helper.h>
38 #include <ns3/tcp-l4-protocol.h>
39 
40 #include <ns3/tcp-congestion-ops.h>
41 
42 #include <ns3/three-gpp-http-client.h>
43 #include <ns3/three-gpp-http-server.h>
44 #include <ns3/three-gpp-http-helper.h>
45 #include <ns3/three-gpp-http-header.h>
46 
47 #include <ns3/basic-data-calculators.h>
48 #include <list>
49 #include <sstream>
50 
51 
52 NS_LOG_COMPONENT_DEFINE ("ThreeGppHttpClientServerTest");
53 
54 using namespace ns3;
55 
56 // HTTP OBJECT TEST CASE //////////////////////////////////////////////////////
57 
58 /**
59  * \ingroup http
60  * \ingroup applications-test
61  * \ingroup tests
62  * A test class which verifies that each HTTP object sent is also received the
63  * same size.
64  *
65  * The test uses a minimalist scenario of one HTTP server and one HTTP client,
66  * connected through a SimpleChannel. The simulation runs until 3 web pages
67  * have been successfully downloaded by the client.
68  *
69  * The test also collects some statistical information from the simulation for
70  * informational or debugging purpose. This can be seen by enabling LOG_INFO.
71  */
72 class ThreeGppHttpObjectTestCase : public TestCase
73 {
74 public:
75   /**
76    * \param name A textual label to briefly describe the test.
77    * \param rngRun Run index to be used, intended to affect the values produced
78    *               by random number generators throughout the test.
79    * \param tcpType Type of TCP algorithm to be used by the connection between
80    *                the client and the server. Must be a child type of
81    *                ns3::TcpSocketFactory.
82    * \param channelDelay Transmission delay between the client and the server
83    *                     (and vice versa) which is due to the channel.
84    * \param bitErrorRate The probability of transmission error between the
85    *                     client and the server (and vice versa) in the unit of
86    *                     bits.
87    * \param mtuSize Maximum transmission unit (in bytes) to be used by the
88    *                server model.
89    * \param useIpv6 If true, IPv6 will be used to address both client and
90    *                server. Otherwise, IPv4 will be used.
91    */
92   ThreeGppHttpObjectTestCase (const std::string &name,
93                               uint32_t           rngRun,
94                               const TypeId      &tcpType,
95                               const Time        &channelDelay,
96                               double            bitErrorRate,
97                               uint32_t          mtuSize,
98                               bool              useIpv6);
99 
100 private:
101   /**
102    * Creates a Node, complete with a TCP/IP stack and address assignment.
103    * #m_tcpType determines the TCP algorithm installed at the TCP stack.
104    * #m_useIpv6 determines whether to use IPv4 addressing or IPv6 addressing.
105    *
106    * \param[in] channel Pointer to a channel which the node's device will be
107    *                    attached to.
108    * \param[out] assignedAddress The resulting address of the node.
109    * \return Pointer to the newly created node.
110    */
111   Ptr<Node> CreateSimpleInternetNode (Ptr<SimpleChannel> channel,
112                                       Address &assignedAddress);
113 
114   // Inherited from TestCase base class.
115   virtual void DoRun ();
116   virtual void DoTeardown ();
117 
118   /**
119    * \internal
120    * Internal class used by ThreeGppHttpObjectTestCase. Keep track of the number
121    * of object and bytes that have been sent and received in the simulation by
122    * listening to the relevant trace sources.
123    */
124   class ThreeGppHttpObjectTracker
125   {
126 public:
127     /// Creates a new instance with all counters begin at zero.
128     ThreeGppHttpObjectTracker ();
129     /**
130      * Shall be invoked when a whole object has been transmitted.
131      * \param size Size of the whole object (in bytes).
132      */
133     void ObjectSent (uint32_t size);
134     /**
135      * Shall be invoked when an object part has been received.
136      * \param size Size of the object part (in bytes). This amount will be
137      *             accumulated until ObjectReceived() is invoked.
138      */
139     void PartReceived (uint32_t size);
140     /**
141      * Shall be invoked after all parts of a complete object have been
142      * received.
143      * \param[out] txSize Size of the whole object (in bytes) when it was
144      *                    transmitted.
145      * \param[out] rxSize Size of the whole object (in bytes) received.
146      * \return True if this receive operation has a matching transmission
147      *         operation (ObjectSent()), otherwise false. Both arguments are
148      *         guaranteed to be replaced with initialized values if the return
149      *         value is true.
150      */
151     bool ObjectReceived (uint32_t &txSize, uint32_t &rxSize);
152     /// \return True if zero object is currently tracked.
153     bool IsEmpty () const;
154     /// \return Number of whole objects that have been received so far.
155     uint16_t GetNumOfObjectsReceived () const;
156 private:
157     /**
158      * Each entry is the size (in bytes) of object transmitted. A new entry is
159      * pushed to the back when a new object is transmitted. The frontmost entry
160      * is then removed when a whole object is received, i.e., it's logically a
161      * first-in-first-out queue data structure.
162      */
163     std::list<uint32_t> m_objectsSize;
164     /// The accumulated size (in bytes) of parts of a whole object.
165     uint32_t m_rxBuffer;
166     /// Number of whole objects that have been received so far.
167     uint16_t m_numOfObjectsReceived;
168   };
169 
170   // The following defines one tracker for each HTTP object type.
171   ThreeGppHttpObjectTracker m_requestObjectTracker;   ///< Tracker of request objects.
172   ThreeGppHttpObjectTracker m_mainObjectTracker;      ///< Tracker of main objects.
173   ThreeGppHttpObjectTracker m_embeddedObjectTracker;  ///< Tracker of embedded objects.
174 
175   // CALLBACK TO TRACE SOURCES.
176 
177   /**
178    * Connected with `TxMainObjectRequest` trace source of the client.
179    * Updates #m_requestObjectTracker.
180    * \param packet The packet of main object sent.
181    */
182   void ClientTxMainObjectRequestCallback (Ptr<const Packet> packet);
183   /**
184    * Connected with `TxEmbeddedObjectRequest` trace source of the client.
185    * Updates #m_requestObjectTracker.
186    * \param packet The packet of embedded object sent.
187    */
188   void ClientTxEmbeddedObjectRequestCallback (Ptr<const Packet> packet);
189   /**
190    * Connected with `Rx` trace source of the server.
191    * Updates #m_requestObjectTracker and perform some tests on the packet and
192    * the size of the object.
193    * \param packet The packet received.
194    * \param from The address where the packet originates from.
195    */
196   void ServerRxCallback (Ptr<const Packet> packet, const Address &from);
197   /**
198    * Connected with `MainObject` trace source of the server.
199    * Updates #m_mainObjectTracker.
200    * \param size Size of the generated main object (in bytes).
201    */
202   void ServerMainObjectCallback (uint32_t size);
203   /**
204    * Connected with `RxMainObjectPacket` trace source of the client.
205    * Updates #m_mainObjectTracker and perform some tests on the packet.
206    * \param packet The packet received.
207    */
208   void ClientRxMainObjectPacketCallback (Ptr<const Packet> packet);
209   /**
210    * Connected with `RxMainObject` trace source of the client. Updates
211    * #m_mainObjectTracker and perform some tests on the size of the object.
212    * \param httpClient Pointer to the application.
213    * \param packet Full packet received by application.
214    */
215   void ClientRxMainObjectCallback (Ptr<const ThreeGppHttpClient> httpClient, Ptr<const Packet> packet);
216   /**
217    * Connected with `EmbeddedObject` trace source of the server.
218    * Updates #m_embeddedObjectTracker.
219    * \param size Size of the generated embedded object (in bytes).
220    */
221   void ServerEmbeddedObjectCallback (uint32_t size);
222   /**
223    * Connected with `RxEmbeddedObjectPacket` trace source of the client.
224    * Updates #m_embeddedObjectTracker and perform some tests on the packet.
225    * \param packet The packet received.
226    */
227   void ClientRxEmbeddedObjectPacketCallback (Ptr<const Packet> packet);
228   /**
229    * Connected with `RxEmbeddedObject` trace source of the client. Updates
230    * #m_embeddedObjectTracker and perform some tests on the size of the object.
231    * \param httpClient Pointer to the application.
232    * \param packet Full packet received by application.
233    */
234   void ClientRxEmbeddedObjectCallback (Ptr<const ThreeGppHttpClient> httpClient, Ptr<const Packet> packet);
235   /**
236    * Connected with `StateTransition` trace source of the client.
237    * Increments #m_numOfPagesReceived when the client enters READING state.
238    * \param oldState The name of the previous state.
239    * \param newState The name of the current state.
240    */
241   void ClientStateTransitionCallback (const std::string &oldState,
242                                       const std::string &newState);
243   /**
244    * Connected with `RxDelay` trace source of the client.
245    * Updates the statistics in #m_delayCalculator.
246    * \param delay The packet one-trip delay time.
247    * \param from The address of the device where the packet originates from.
248    */
249   void ClientRxDelayCallback (const Time &delay, const Address &from);
250   /**
251    * Connected with `RxRtt` trace source of the client.
252    * Updates the statistics in #m_rttCalculator.
253    * \param rtt The packet round trip delay time.
254    * \param from The address of the device where the packet originates from.
255    */
256   void ClientRxRttCallback (const Time &rtt, const Address &from);
257   /**
258    * Connected with `PhyRxDrop` trace source of both the client's and server's
259    * devices. Increments #m_numOfPacketDrops.
260    * \param packet Pointer to the packet being dropped.
261    */
262   void DeviceDropCallback (Ptr<const Packet> packet);
263   /**
264    * Dummy event
265    */
266   void ProgressCallback ();
267 
268   // THE PARAMETERS OF THE TEST CASE.
269 
270   uint32_t  m_rngRun;       ///< Determines the set of random values generated.
271   TypeId    m_tcpType;      ///< TCP algorithm used.
272   Time      m_channelDelay; ///< %Time needed by a packet to propagate.
273   uint32_t  m_mtuSize;      ///< Maximum transmission unit (in bytes).
274   bool      m_useIpv6;      ///< Whether to use IPv6 or IPv4.
275 
276   // OTHER MEMBER VARIABLES.
277 
278   /// Receive error model to be attached to the devices of both directions.
279   Ptr<RateErrorModel> m_errorModel;
280   /// Begins with 0. Simulation stops if this reaches 3.
281   uint16_t            m_numOfPagesReceived;
282   /// Number of packets dropped because of #m_errorModel.
283   uint16_t            m_numOfPacketDrops;
284   /// Installs TCP/IP stack on the nodes.
285   InternetStackHelper m_internetStackHelper;
286   /// Assigns IPv4 addresses to the nodes.
287   Ipv4AddressHelper   m_ipv4AddressHelper;
288   /// Assigns IPv6 addresses to the nodes.
289   Ipv6AddressHelper   m_ipv6AddressHelper;
290   /// Keeps statistical information of one-trip delays (in seconds).
291   Ptr<MinMaxAvgTotalCalculator<double> > m_delayCalculator;
292   /// Keeps statistical information of round-trip delays (in seconds).
293   Ptr<MinMaxAvgTotalCalculator<double> > m_rttCalculator;
294 
295 }; // end of `class HttpClientServerTestCase`
296 
ThreeGppHttpObjectTestCase(const std::string & name,uint32_t rngRun,const TypeId & tcpType,const Time & channelDelay,double bitErrorRate,uint32_t mtuSize,bool useIpv6)297 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTestCase (const std::string &name,
298                                                         uint32_t           rngRun,
299                                                         const TypeId      &tcpType,
300                                                         const Time        &channelDelay,
301                                                         double            bitErrorRate,
302                                                         uint32_t          mtuSize,
303                                                         bool              useIpv6)
304   : TestCase (name),
305   m_rngRun (rngRun),
306   m_tcpType (tcpType),
307   m_channelDelay (channelDelay),
308   m_mtuSize (mtuSize),
309   m_useIpv6 (useIpv6),
310   m_numOfPagesReceived (0),
311   m_numOfPacketDrops (0)
312 {
313   NS_LOG_FUNCTION (this << GetName ());
314 
315   //NS_ASSERT (tcpType.IsChildOf (TypeId::LookupByName ("ns3::TcpSocketBase")));
316   NS_ASSERT (channelDelay.IsPositive ());
317 
318   m_errorModel = CreateObject<RateErrorModel> ();
319   m_errorModel->SetRate (bitErrorRate);
320   m_errorModel->SetUnit (RateErrorModel::ERROR_UNIT_BIT);
321 
322   m_ipv4AddressHelper.SetBase (Ipv4Address ("10.0.0.0"),
323                                Ipv4Mask ("255.0.0.0"),
324                                Ipv4Address ("0.0.0.1"));
325   m_ipv6AddressHelper.SetBase (Ipv6Address ("2001:1::"),
326                                Ipv6Prefix (64),
327                                Ipv6Address ("::1"));
328 
329   m_delayCalculator = CreateObject<MinMaxAvgTotalCalculator<double> > ();
330   m_rttCalculator = CreateObject<MinMaxAvgTotalCalculator<double> > ();
331 }
332 
333 Ptr<Node>
CreateSimpleInternetNode(Ptr<SimpleChannel> channel,Address & assignedAddress)334 ThreeGppHttpObjectTestCase::CreateSimpleInternetNode (Ptr<SimpleChannel> channel,
335                                                       Address &assignedAddress)
336 {
337   NS_LOG_FUNCTION (this << channel);
338 
339   Ptr<SimpleNetDevice> dev = CreateObject<SimpleNetDevice> ();
340   dev->SetAddress (Mac48Address::Allocate ());
341   dev->SetChannel (channel);
342   dev->SetReceiveErrorModel (m_errorModel);
343 
344   Ptr<Node> node = CreateObject<Node> ();
345   node->AddDevice (dev);
346   m_internetStackHelper.Install (node);
347 
348   // Assign IP address according to the selected Ip version.
349   if (m_useIpv6)
350     {
351       Ipv6InterfaceContainer ipv6Ifs
352         = m_ipv6AddressHelper.Assign (NetDeviceContainer (dev));
353       NS_ASSERT (ipv6Ifs.GetN () == 1);
354       assignedAddress = ipv6Ifs.GetAddress (0, 0);
355     }
356   else
357     {
358       Ipv4InterfaceContainer ipv4Ifs
359         = m_ipv4AddressHelper.Assign (NetDeviceContainer (dev));
360       NS_ASSERT (ipv4Ifs.GetN () == 1);
361       assignedAddress = ipv4Ifs.GetAddress (0, 0);
362     }
363 
364   NS_LOG_DEBUG (this << " node is assigned to " << assignedAddress << ".");
365 
366   // Set the TCP algorithm.
367   Ptr<TcpL4Protocol> tcp = node->GetObject<TcpL4Protocol> ();
368   tcp->SetAttribute ("SocketType", TypeIdValue (m_tcpType));
369 
370   // Connect with the trace source that informs about packet drop due to error.
371   dev->TraceConnectWithoutContext (
372     "PhyRxDrop",
373     MakeCallback (&ThreeGppHttpObjectTestCase::DeviceDropCallback, this));
374 
375   return node;
376 }
377 
378 void
DoRun()379 ThreeGppHttpObjectTestCase::DoRun ()
380 {
381   NS_LOG_FUNCTION (this << GetName ());
382   Config::SetGlobal ("RngRun", UintegerValue (m_rngRun));
383   NS_LOG_INFO (this << " Running test case " << GetName ());
384 
385   /*
386    * Create topology:
387    *
388    *     Server Node                  Client Node
389    * +-----------------+          +-----------------+
390    * |   HTTP Server   |          |   HTTP Client   |
391    * |   Application   |          |   Application   |
392    * +-----------------+          +-----------------+
393    * |       TCP       |          |       TCP       |
394    * +-----------------+          +-----------------+
395    * |     IPv4/v6     |          |     IPv4/v6     |
396    * +-----------------+          +-----------------+
397    * |  Simple NetDev  |          |  Simple NetDev  |
398    * +-----------------+          +-----------------+
399    *          |                            |
400    *          |                            |
401    *          +----------------------------+
402    *                  Simple Channel
403    */
404 
405   // Channel.
406   Ptr<SimpleChannel> channel = CreateObject<SimpleChannel> ();
407   channel->SetAttribute ("Delay", TimeValue (m_channelDelay));
408 
409   // Server node.
410   Address serverAddress;
411   Ptr<Node> serverNode = CreateSimpleInternetNode (channel, serverAddress);
412   ThreeGppHttpServerHelper serverHelper (serverAddress);
413   ApplicationContainer serverApplications = serverHelper.Install (serverNode);
414   NS_TEST_ASSERT_MSG_EQ (serverApplications.GetN (), 1,
415                          "Invalid number of HTTP servers has been installed");
416   Ptr<ThreeGppHttpServer> httpServer = serverApplications.Get (0)->GetObject<ThreeGppHttpServer> ();
417   NS_TEST_ASSERT_MSG_NE (httpServer, 0,
418                          "HTTP server installation fails to produce a proper type");
419   httpServer->SetMtuSize (m_mtuSize);
420 
421   // Client node.
422   Address clientAddress;
423   Ptr<Node> clientNode = CreateSimpleInternetNode (channel, clientAddress);
424   ThreeGppHttpClientHelper clientHelper (serverAddress);
425   ApplicationContainer clientApplications = clientHelper.Install (clientNode);
426   NS_TEST_ASSERT_MSG_EQ (clientApplications.GetN (), 1,
427                          "Invalid number of HTTP clients has been installed");
428   Ptr<ThreeGppHttpClient> httpClient = clientApplications.Get (0)->GetObject<ThreeGppHttpClient> ();
429   NS_TEST_ASSERT_MSG_NE (httpClient, 0,
430                          "HTTP client installation fails to produce a proper type");
431 
432   // Uplink (requests) trace sources.
433   bool traceSourceConnected = httpClient->TraceConnectWithoutContext (
434       "TxMainObjectRequest",
435       MakeCallback (&ThreeGppHttpObjectTestCase::ClientTxMainObjectRequestCallback,
436                     this));
437   NS_ASSERT (traceSourceConnected);
438   traceSourceConnected = httpClient->TraceConnectWithoutContext (
439       "TxEmbeddedObjectRequest",
440       MakeCallback (&ThreeGppHttpObjectTestCase::ClientTxEmbeddedObjectRequestCallback,
441                     this));
442   NS_ASSERT (traceSourceConnected);
443   traceSourceConnected = httpServer->TraceConnectWithoutContext (
444       "Rx",
445       MakeCallback (&ThreeGppHttpObjectTestCase::ServerRxCallback,
446                     this));
447   NS_ASSERT (traceSourceConnected);
448 
449   // Downlink (main objects) trace sources.
450   traceSourceConnected = httpServer->TraceConnectWithoutContext (
451       "MainObject",
452       MakeCallback (&ThreeGppHttpObjectTestCase::ServerMainObjectCallback,
453                     this));
454   NS_ASSERT (traceSourceConnected);
455   traceSourceConnected = httpClient->TraceConnectWithoutContext (
456       "RxMainObjectPacket",
457       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxMainObjectPacketCallback,
458                     this));
459   NS_ASSERT (traceSourceConnected);
460   traceSourceConnected = httpClient->TraceConnectWithoutContext (
461       "RxMainObject",
462       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxMainObjectCallback,
463                     this));
464   NS_ASSERT (traceSourceConnected);
465 
466   // Downlink (embedded objects) trace sources.
467   traceSourceConnected = httpServer->TraceConnectWithoutContext (
468       "EmbeddedObject",
469       MakeCallback (&ThreeGppHttpObjectTestCase::ServerEmbeddedObjectCallback,
470                     this));
471   NS_ASSERT (traceSourceConnected);
472 
473   traceSourceConnected = httpClient->TraceConnectWithoutContext (
474       "RxEmbeddedObjectPacket",
475       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectPacketCallback,
476                     this));
477   NS_ASSERT (traceSourceConnected);
478 
479   traceSourceConnected = httpClient->TraceConnectWithoutContext (
480       "RxEmbeddedObject",
481       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectCallback,
482                     this));
483   NS_ASSERT (traceSourceConnected);
484 
485   // Other trace sources.
486   traceSourceConnected = httpClient->TraceConnectWithoutContext (
487       "StateTransition",
488       MakeCallback (&ThreeGppHttpObjectTestCase::ClientStateTransitionCallback,
489                     this));
490   NS_ASSERT (traceSourceConnected);
491   traceSourceConnected = httpClient->TraceConnectWithoutContext (
492       "RxDelay",
493       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxDelayCallback,
494                     this));
495   NS_ASSERT (traceSourceConnected);
496   traceSourceConnected = httpClient->TraceConnectWithoutContext (
497       "RxRtt",
498       MakeCallback (&ThreeGppHttpObjectTestCase::ClientRxRttCallback,
499                     this));
500   NS_ASSERT (traceSourceConnected);
501 
502   Simulator::Schedule (Seconds (1.0), &ThreeGppHttpObjectTestCase::ProgressCallback, this);
503 
504   /*
505    * Here we don't set the simulation stop time. During the run, the simulation
506    * will stop immediately after the client has completely received the third
507    * web page.
508    */
509   Simulator::Run ();
510 
511   // Dump some statistical information about the simulation.
512   NS_LOG_INFO (this << " Total request objects received: "
513                     << m_requestObjectTracker.GetNumOfObjectsReceived ()
514                     << " object(s).");
515   NS_LOG_INFO (this << " Total main objects received: "
516                     << m_mainObjectTracker.GetNumOfObjectsReceived ()
517                     << " object(s).");
518   NS_LOG_INFO (this << " Total embedded objects received: "
519                     << m_embeddedObjectTracker.GetNumOfObjectsReceived ()
520                     << " object(s).");
521   NS_LOG_INFO (this << " One-trip delays:"
522                     << " average=" << m_delayCalculator->getMean ()
523                     << " min=" << m_delayCalculator->getMin ()
524                     << " max=" << m_delayCalculator->getMax ());
525   NS_LOG_INFO (this << " Round-trip delays:"
526                     << " average=" << m_rttCalculator->getMean ()
527                     << " min=" << m_rttCalculator->getMin ()
528                     << " max=" << m_rttCalculator->getMax ());
529   NS_LOG_INFO (this << " Number of packets dropped by the devices: "
530                     << m_numOfPacketDrops << " packet(s).");
531 
532   // Some post-simulation tests.
533   NS_TEST_EXPECT_MSG_EQ (m_numOfPagesReceived, 3,
534                          "Unexpected number of web pages processed.");
535   NS_TEST_EXPECT_MSG_EQ (m_requestObjectTracker.IsEmpty (), true,
536                          "Tracker of request objects detected irrelevant packet(s).");
537   NS_TEST_EXPECT_MSG_EQ (m_mainObjectTracker.IsEmpty (), true,
538                          "Tracker of main objects detected irrelevant packet(s).");
539   NS_TEST_EXPECT_MSG_EQ (m_embeddedObjectTracker.IsEmpty (), true,
540                          "Tracker of embedded objects detected irrelevant packet(s).");
541 
542   Simulator::Destroy ();
543 
544 } // end of `void HttpClientServerTestCase::DoRun ()`
545 
546 void
DoTeardown()547 ThreeGppHttpObjectTestCase::DoTeardown ()
548 {
549   NS_LOG_FUNCTION (this << GetName ());
550 }
551 
ThreeGppHttpObjectTracker()552 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ThreeGppHttpObjectTracker ()
553   : m_rxBuffer (0),
554   m_numOfObjectsReceived (0)
555 {
556   NS_LOG_FUNCTION (this);
557 }
558 
559 void
ObjectSent(uint32_t size)560 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ObjectSent (uint32_t size)
561 {
562   NS_LOG_FUNCTION (this << size);
563   m_objectsSize.push_back (size);
564 }
565 
566 void
PartReceived(uint32_t size)567 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::PartReceived (uint32_t size)
568 {
569   NS_LOG_FUNCTION (this << size);
570   m_rxBuffer += size;
571 }
572 
573 bool
ObjectReceived(uint32_t & txSize,uint32_t & rxSize)574 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::ObjectReceived (uint32_t &txSize,
575                                                                        uint32_t &rxSize)
576 {
577   NS_LOG_FUNCTION (this);
578 
579   if (m_objectsSize.empty ())
580     {
581       return false;
582     }
583 
584   // Set output values.
585   txSize = m_objectsSize.front ();
586   rxSize = m_rxBuffer;
587 
588   // Reset counters.
589   m_objectsSize.pop_front ();
590   m_rxBuffer = 0;
591   m_numOfObjectsReceived++;
592 
593   return true;
594 }
595 
596 bool
IsEmpty() const597 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::IsEmpty () const
598 {
599   return (m_objectsSize.empty () && (m_rxBuffer == 0));
600 }
601 
602 uint16_t
GetNumOfObjectsReceived() const603 ThreeGppHttpObjectTestCase::ThreeGppHttpObjectTracker::GetNumOfObjectsReceived () const
604 {
605   return m_numOfObjectsReceived;
606 }
607 
608 void
ClientTxMainObjectRequestCallback(Ptr<const Packet> packet)609 ThreeGppHttpObjectTestCase::ClientTxMainObjectRequestCallback (Ptr<const Packet> packet)
610 {
611   NS_LOG_FUNCTION (this << packet << packet->GetSize ());
612   m_requestObjectTracker.ObjectSent (packet->GetSize ());
613 }
614 
615 void
ClientTxEmbeddedObjectRequestCallback(Ptr<const Packet> packet)616 ThreeGppHttpObjectTestCase::ClientTxEmbeddedObjectRequestCallback (Ptr<const Packet> packet)
617 {
618   NS_LOG_FUNCTION (this << packet << packet->GetSize ());
619   m_requestObjectTracker.ObjectSent (packet->GetSize ());
620 }
621 
622 void
ServerRxCallback(Ptr<const Packet> packet,const Address & from)623 ThreeGppHttpObjectTestCase::ServerRxCallback (Ptr<const Packet> packet,
624                                               const Address &from)
625 {
626   NS_LOG_FUNCTION (this << packet << packet->GetSize () << from);
627 
628   // Check the header in packet
629   Ptr<Packet> copy = packet->Copy ();
630   ThreeGppHttpHeader httpHeader;
631   NS_TEST_ASSERT_MSG_EQ (copy->RemoveHeader (httpHeader), 22,
632                          "Error finding ThreeGppHttpHeader in a packet received by the server");
633   NS_TEST_ASSERT_MSG_GT (httpHeader.GetClientTs (), Seconds (0.0),
634                          "Request object's client TS is unexpectedly non-positive");
635 
636   m_requestObjectTracker.PartReceived (packet->GetSize ());
637 
638   /*
639    * Request objects are assumed to be small and to not typically split. So we
640    * immediately follow by concluding the receive of a whole request object.
641    */
642   uint32_t txSize;
643   uint32_t rxSize;
644   bool isSent = m_requestObjectTracker.ObjectReceived (txSize, rxSize);
645   NS_TEST_ASSERT_MSG_EQ (isSent, true,
646                          "Server receives one too many request object");
647   NS_TEST_ASSERT_MSG_EQ (txSize, rxSize,
648                          "Transmitted size and received size of request object differ");
649 }
650 
651 void
ServerMainObjectCallback(uint32_t size)652 ThreeGppHttpObjectTestCase::ServerMainObjectCallback (uint32_t size)
653 {
654   NS_LOG_FUNCTION (this << size);
655   m_mainObjectTracker.ObjectSent (size);
656 }
657 
658 void
ClientRxMainObjectPacketCallback(Ptr<const Packet> packet)659 ThreeGppHttpObjectTestCase::ClientRxMainObjectPacketCallback (Ptr<const Packet> packet)
660 {
661   NS_LOG_FUNCTION (this << packet << packet->GetSize ());
662   m_mainObjectTracker.PartReceived (packet->GetSize ());
663 }
664 
665 void
ClientRxMainObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,Ptr<const Packet> packet)666 ThreeGppHttpObjectTestCase::ClientRxMainObjectCallback (Ptr<const ThreeGppHttpClient> httpClient,
667                                                         Ptr<const Packet> packet)
668 {
669   NS_LOG_FUNCTION (this << httpClient << httpClient->GetNode ()->GetId ());
670 
671   // Verify the header in the packet.
672   Ptr<Packet> copy = packet->Copy ();
673   ThreeGppHttpHeader httpHeader;
674   NS_TEST_ASSERT_MSG_EQ (copy->RemoveHeader (httpHeader), 22,
675                          "Error finding ThreeGppHttpHeader in a packet received by the server");
676   NS_TEST_ASSERT_MSG_EQ (httpHeader.GetContentType (), ThreeGppHttpHeader::MAIN_OBJECT,
677                          "Invalid content type in the received packet");
678   NS_TEST_ASSERT_MSG_GT (httpHeader.GetClientTs (), Seconds (0.0),
679                          "Main object's client TS is unexpectedly non-positive");
680   NS_TEST_ASSERT_MSG_GT (httpHeader.GetServerTs (), Seconds (0.0),
681                          "Main object's server TS is unexpectedly non-positive");
682   uint32_t txSize;
683   uint32_t rxSize;
684   bool isSent = m_mainObjectTracker.ObjectReceived (txSize, rxSize);
685   NS_TEST_ASSERT_MSG_EQ (isSent, true,
686                          "Client receives one too many main object");
687   NS_TEST_ASSERT_MSG_EQ (txSize, rxSize,
688                          "Transmitted size and received size of main object differ");
689   NS_TEST_ASSERT_MSG_EQ (httpHeader.GetContentLength (), rxSize,
690                          "Actual main object packet size and received size of main object differ");
691 }
692 
693 void
ServerEmbeddedObjectCallback(uint32_t size)694 ThreeGppHttpObjectTestCase::ServerEmbeddedObjectCallback (uint32_t size)
695 {
696   NS_LOG_FUNCTION (this << size);
697   m_embeddedObjectTracker.ObjectSent (size);
698 }
699 
700 void
ClientRxEmbeddedObjectPacketCallback(Ptr<const Packet> packet)701 ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectPacketCallback (Ptr<const Packet> packet)
702 {
703   NS_LOG_FUNCTION (this << packet << packet->GetSize ());
704   m_embeddedObjectTracker.PartReceived (packet->GetSize ());
705 }
706 
707 void
ClientRxEmbeddedObjectCallback(Ptr<const ThreeGppHttpClient> httpClient,Ptr<const Packet> packet)708 ThreeGppHttpObjectTestCase::ClientRxEmbeddedObjectCallback (Ptr<const ThreeGppHttpClient> httpClient,
709                                                             Ptr<const Packet> packet)
710 {
711   NS_LOG_FUNCTION (this << httpClient << httpClient->GetNode ()->GetId ());
712 
713   // Verify the header in the packet.
714   Ptr<Packet> copy = packet->Copy ();
715   ThreeGppHttpHeader httpHeader;
716   NS_TEST_ASSERT_MSG_EQ (copy->RemoveHeader (httpHeader), 22,
717                          "Error finding ThreeGppHttpHeader in a packet received by the server");
718   NS_TEST_ASSERT_MSG_EQ (httpHeader.GetContentType (), ThreeGppHttpHeader::EMBEDDED_OBJECT,
719                          "Invalid content type in the received packet");
720   NS_TEST_ASSERT_MSG_GT (httpHeader.GetClientTs (), Seconds (0.0),
721                          "Embedded object's client TS is unexpectedly non-positive");
722   NS_TEST_ASSERT_MSG_GT (httpHeader.GetServerTs (), Seconds (0.0),
723                          "Embedded object's server TS is unexpectedly non-positive");
724 
725   uint32_t txSize;
726   uint32_t rxSize;
727   bool isSent = m_embeddedObjectTracker.ObjectReceived (txSize, rxSize);
728   NS_TEST_ASSERT_MSG_EQ (isSent, true,
729                          "Client receives one too many embedded object");
730   NS_TEST_ASSERT_MSG_EQ (txSize, rxSize,
731                          "Transmitted size and received size of embedded object differ");
732   NS_TEST_ASSERT_MSG_EQ (httpHeader.GetContentLength (), rxSize,
733                          "Actual embedded object packet size and received size of embedded object differ");
734 }
735 
736 void
ClientStateTransitionCallback(const std::string & oldState,const std::string & newState)737 ThreeGppHttpObjectTestCase::ClientStateTransitionCallback (const std::string &oldState,
738                                                            const std::string &newState)
739 {
740   NS_LOG_FUNCTION (this << oldState << newState);
741 
742   if (newState == "READING")
743     {
744       m_numOfPagesReceived++;
745 
746       if (m_numOfPagesReceived >= 3)
747         {
748           // We have processed 3 web pages and that should be enough for this test.
749           NS_LOG_LOGIC (this << " Test is stopping now.");
750           Simulator::Stop ();
751         }
752     }
753 }
754 
755 void
ProgressCallback()756 ThreeGppHttpObjectTestCase::ProgressCallback ()
757 {
758   NS_LOG_INFO ("Simulator time now: " << Simulator::Now ().As (Time::S) << ".");
759   Simulator::Schedule (Seconds (1.0), &ThreeGppHttpObjectTestCase::ProgressCallback, this);
760 }
761 
762 void
ClientRxDelayCallback(const Time & delay,const Address & from)763 ThreeGppHttpObjectTestCase::ClientRxDelayCallback (const Time &delay,
764                                                    const Address &from)
765 {
766   NS_LOG_FUNCTION (this << delay.As (Time::S) << from);
767   m_delayCalculator->Update (delay.GetSeconds ());
768 }
769 
770 void
ClientRxRttCallback(const Time & rtt,const Address & from)771 ThreeGppHttpObjectTestCase::ClientRxRttCallback (const Time &rtt,
772                                                  const Address &from)
773 {
774   NS_LOG_FUNCTION (this << rtt.As (Time::S) << from);
775   m_rttCalculator->Update (rtt.GetSeconds ());
776 }
777 
778 void
DeviceDropCallback(Ptr<const Packet> packet)779 ThreeGppHttpObjectTestCase::DeviceDropCallback (Ptr<const Packet> packet)
780 {
781   NS_LOG_FUNCTION (this << packet << packet->GetSize ());
782   m_numOfPacketDrops++;
783 }
784 
785 
786 // TEST SUITE /////////////////////////////////////////////////////////////////
787 
788 /**
789  * \ingroup http
790  * \ingroup applications-test
791  * \ingroup tests
792  * A test class for running several system tests which validate the web
793  * browsing traffic model.
794  *
795  * The tests cover the combinations of the following parameters:
796  *   - the use of NewReno (ns-3's default)
797  *   - various lengths of channel delay: 3 ms, 30 ms, and 300 ms;
798  *   - the existence of transmission error;
799  *   - different MTU (maximum transmission unit) sizes;
800  *   - IPv4 and IPv6; and
801  *   - the use of different set of random numbers.
802  *
803  * The _fullness_ parameter specified when running the test framework will
804  * determine the number of test cases created by this test suite.
805  */
806 class ThreeGppHttpClientServerTestSuite : public TestSuite
807 {
808 public:
809   /// Instantiate the test suite.
ThreeGppHttpClientServerTestSuite()810   ThreeGppHttpClientServerTestSuite () : TestSuite ("three-gpp-http-client-server-test", SYSTEM)
811   {
812     // LogComponentEnable ("ThreeGppHttpClientServerTest", LOG_INFO);
813     // LogComponentEnable ("ThreeGppHttpClient", LOG_INFO);
814     // LogComponentEnable ("ThreeGppHttpServer", LOG_INFO);
815     // LogComponentEnableAll (LOG_PREFIX_ALL);
816 
817     Time channelDelay[] = {
818       MilliSeconds (3),
819       MilliSeconds (30),
820       MilliSeconds (300)
821     };
822     double bitErrorRate[] = {0.0, 5.0e-6};
823     uint32_t mtuSize[] = {536, 1460};
824 
825     uint32_t run = 1;
826     while (run <= 100)
827       {
828         for (uint32_t i1 = 0; i1 < 3; i1++)
829           {
830             for (uint32_t i2 = 0; i2 < 2; i2++)
831               {
832                 for (uint32_t i3 = 0; i3 < 2; i3++)
833                   {
834                     AddHttpObjectTestCase (run++,
835                                            channelDelay[i1],
836                                            bitErrorRate[i2],
837                                            mtuSize[i3],
838                                            false);
839                     AddHttpObjectTestCase (run++,
840                                            channelDelay[i1],
841                                            bitErrorRate[i2],
842                                            mtuSize[i3],
843                                            true);
844                   }
845               }
846           }
847       }
848   }
849 
850 private:
851   /**
852    * Creates a test case with the given parameters.
853    *
854    * \param rngRun Run index to be used, intended to affect the values produced
855    *               by random number generators throughout the test.
856    * \param channelDelay Transmission delay between the client and the server
857    *                     (and vice versa) which is due to the channel.
858    * \param bitErrorRate The probability of transmission error between the
859    *                     client and the server (and vice versa) in the unit of
860    *                     bits.
861    * \param mtuSize Maximum transmission unit (in bytes) to be used by the
862    *                server model.
863    * \param useIpv6 If true, IPv6 will be used to address both client and
864    *                server. Otherwise, IPv4 will be used.
865    */
AddHttpObjectTestCase(uint32_t rngRun,const Time & channelDelay,double bitErrorRate,uint32_t mtuSize,bool useIpv6)866   void AddHttpObjectTestCase (uint32_t       rngRun,
867                               const Time    &channelDelay,
868                               double        bitErrorRate,
869                               uint32_t      mtuSize,
870                               bool          useIpv6)
871   {
872     std::ostringstream name;
873     name << "Run #" << rngRun;
874     name << " delay=" << channelDelay.As (Time::MS);
875     name << " ber=" << bitErrorRate;
876     name << " mtu=" << mtuSize;
877 
878     if (useIpv6)
879       {
880         name << " IPv6";
881       }
882     else
883       {
884         name << " IPv4";
885       }
886 
887     // Assign higher fullness for tests with higher RngRun.
888     TestCase::TestDuration testDuration = TestCase::QUICK;
889     if (rngRun > 20)
890       {
891         testDuration = TestCase::EXTENSIVE;
892       }
893     if (rngRun > 50)
894       {
895         testDuration = TestCase::TAKES_FOREVER;
896       }
897 
898     AddTestCase (new ThreeGppHttpObjectTestCase (name.str (),
899                                                  rngRun,
900                                                  TcpNewReno::GetTypeId (),
901                                                  channelDelay,
902                                                  bitErrorRate,
903                                                  mtuSize,
904                                                  useIpv6),
905                  testDuration);
906   }
907 
908 }; // end of class `ThreeGppHttpClientServerTestSuite`
909 
910 /// The global instance of the `three-gpp-http-client-server` system test.
911 static ThreeGppHttpClientServerTestSuite g_httpClientServerTestSuiteInstance;
912 
913