#------------------------------------------------------------------------------
# Statefull TCP tutorial
#------------------------------------------------------------------------------
#
# This tutorial demonstrates a statefull TCP test.
# In ByteBlower, the HTTP Layer4 protocol is used to test statefull TCP.
#
# The tutorial will show you how to setup the test and obtain test results
# using the ByteBlower LowerLayer API.
#
# As in normal HTTP sessions, most data will flow from the server to the client.
# So, the TCP source is the HTTP server and the TCP destination is the HTTP client.
#
# @author ByteBlower development team <byteblower@excentis.com>
# @date 2008/05/06
#
# file reference: tutorial.statefull-tcp.tcl
#------------------------------------------------------------------------------

# --- Life is boring without the ByteBlower package
package require ByteBlower

#
# --- Define configuration parameters ---
#
#- Define the IP address or hostname of the ByteBlower server to use
set serverAddress byteblower-6.lab.excentis.com
#- Define the physical port you want to use on the ByteBlower server.
set physicalPort1 trunk-1-1
set physicalPort2 trunk-1-2

#- Define source port (server) Layer2 MAC Address
set serverMacAddress "00:FF:12:00:00:01"

#- Define destination port (client) Layer2 MAC Address
set clientMacAddress "00:FF:12:00:00:02"

#- Define source port (server) Layer3 IPv4 settings
#- will we use DHCP?
set serverUseDhcp 0
#- Fixed IPv4 settings (used when DHCP is NOT used)
set serverIpAddress "10.10.0.2"
set serverNetmask "255.255.255.0"
set serverIpGW "10.10.0.1"

#- Define destination port (client) Layer3 settings
#- will we use DHCP?
set clientUseDhcp 0
#- Fixed IPv4 settings (used when DHCP is NOT used)
set clientIpAddress "10.10.0.3"
set clientNetmask "255.255.255.0"
set clientIpGW "10.10.0.1"

#- We can specify the TCP port, used by server and client.
set serverTcpPort 80
set clientTcpPort 6666

#- The HTTP client request for a 'number of bytes',
#  the HTTP server will then push data until the page size is complete
set requestSize 100000000 ;# 100 MB

#
# --- Preparing the test ---
#

#- Connect to the ByteBlower server
set server [ ByteBlower Server.Add $serverAddress ]

#- Create 2 logical ByteBlower Ports
set httpServerPort [ $server Port.Create $physicalPort1 ]
set httpClientPort [ $server Port.Create $physicalPort2 ]

#- Source port Layer2 setup
#- Create the Layer2 Configuration Object
set serverL2 [ $httpServerPort Layer2.Set ethII ]
#- Set the MAC address on the Layer2 Object
$serverL2 Mac.Set $serverMacAddress

#- Source port Layer2 setup
#- Create the Layer2 Configuration Object
set clientL2 [ $httpClientPort Layer2.Set ethII ]
#- Set the MAC address on the Layer2 Object
$clientL2 Mac.Set $clientMacAddress

#- Source port (HTTP server) Layer3 setup
#- Create the Layer3 Configuration Object
set serverL3 [ $httpServerPort Layer3.Set ipv4 ]
if { $serverUseDhcp == 1 } {
    #- Using DHCP
    #- Perform DHCPv4 on the dhcp Object
    [ $serverL3 Dhcp ] Perform
} else {
    #- Using static IP
    #- Set IPv4 address, Netmask and gateway on the Layer3 Object
    $serverL3 Ip.Set $serverIpAddress
    $serverL3 Netmask.Set $serverNetmask
    $serverL3 Gateway.Set $serverIpGW
}

#- Destination port (HTTP client) Layer3 setup
#- Create the Layer3 Configuration Object
set clientL3 [ $httpClientPort Layer3.Set ipv4 ]
if { $clientUseDhcp == 1 } {
    #- Using DHCP
    #- Perform DHCPv4 on the dhcp Object
    [ $clientL3 Dhcp ] Perform
} else {
    #- Using static IP
    #- Set IPv4 address, Netmask and gateway on the Layer3 Object
    $clientL3 Ip.Set $clientIpAddress
    $clientL3 Netmask.Set $clientNetmask
    $clientL3 Gateway.Set $clientIpGW
}

#
# --- Create a HTTP server ---
#
#- Create the HTTP server Object
set httpServer [ $httpServerPort Protocol.Http.Server.Add ]
#- Force the HTTP server to use a specified TCP port
$httpServer Port.Set $serverTcpPort

#
# --- Create a HTTP client ---
#
#- Create the client Object
set httpClient [ $httpClientPort Protocol.Http.Client.Add ]
#- Set the server IP address and TCP port to connect to
$httpClient Remote.Address.Set [ $serverL3 Ip.Get ]
$httpClient Remote.Port.Set [ $httpServer Port.Get ]
#- Force the HTTP client to use a specified TCP port
$httpClient Local.Port.Set $clientTcpPort

#- Descriptions
puts ""
puts "*** ByteBlower server Information ***"
puts ""
puts [ $server Description.Get ]
puts ""
puts "*** ByteBlower TCP source (HTTP server) Port ***"
puts ""
puts [ $httpServerPort Description.Get ]
puts ""
puts "*** ByteBlower TCP destination (HTTP client) Port ***"
puts ""
puts [ $httpClientPort Description.Get ]

#
# --- Perform the Statefull TCP test ---
#
#- Start the HTTP server
$httpServer Start

#- Start the HTTP client (request a page size to the configure remote IP address / port)
$httpClient Request.Page $requestSize

# Wait for the client to complete the request
puts "Statefull TCP test started at [ clock format [ clock seconds ] ] (finish unknown because of TCP)"
puts "Waiting for the TCP session to finish"
while { [ string equal [ $httpClient StatusValue.Get ] "active" ] } {
    set wait 0
    after 1000 "set wait 1"
    vwait wait
    puts -nonewline "*"; flush stdout
    unset wait
}
puts ""
puts "Statefull TCP test finished at [ clock format [ clock seconds ] ]"
puts ""


#- Stop the HTTP client
puts [ $httpClient Stop ]

#- Stop the HTTP stop
$httpServer Stop

#
# --- Getting test results ---
#

##
# Procedure to get HTTP client Status information.
# The information is shown on stdout
#
# @param httpClient HTTP client to get the status information from
# @param requestSize The size of the requested page
#
proc showHttpClientInformation {httpClient requestSize} {
    foreach {name value} [ $httpClient Status.Get ] {
        switch -- [ string toupper $name ] {
            T1 -
            T2 -
            T3 {
                puts [ format " * %-25s : %d seconds %d milliseconds" ${name} [ lindex ${value} 0 ] [ lindex ${value} 1 ] ]
            }
            default {
                #- These values will be obtained later in this procedure
                #puts [ format " * %-25s : %s" ${name} ${value} ]
            }
        }
    }

    #- The final status value of the HTTP client, we expect it to be "finished"
    set statusValue [ $httpClient StatusValue.Get ]

    #- Received number of bytes
    set rxBytes [ $httpClient ReceivedSize.Get ]
    #- Get the average throughput, minimum and maximum TCP window size
    set avgThroughput [ $httpClient AverageThroughput.Get ]
    set minCongestion [ $httpClient MinimumWindowSize.Get ]
    set maxCongestion [ $httpClient MaximumWindowSize.Get ]

    puts [ format " * %-25s : %d bytes" "TX Payload" $requestSize ]
    puts [ format " * %-25s : %d bytes" "RX Payload" $rxBytes ]
    puts [ format " * %-25s : %f bytes/s" "Average Throughput" $avgThroughput ]
    puts [ format " * %-25s : %d bytes" "Min Congestion Window" $minCongestion ]
    puts [ format " * %-25s : %d bytes" "Max Congestion Window" $maxCongestion ]
    puts [ format " * %-25s : %s" "Final Status" $statusValue ]
    puts ""

    return

}

##
# Small procedure to get HTTP client Status information
# from the HTTP server side.
# The information is shown on stdout
#
# @param httpServer HTTP server to get the status information from
# @param httpClient HTTP client to get the information for on the server
#
proc showHttpServerClientInformation {httpServer httpClient} {
    set scId [ $httpClient ServerClientId.Get ]
    set serverClientInfo [ $httpServer Client.Status.Get $scId ]
    foreach { name value } $serverClientInfo {
        switch -- [ string toupper $name ] {
            T1 -
            T2 -
            T3 {
                puts [ format " * %-25s : %d seconds %d milliseconds" ${name} [ lindex ${value} 0 ] [ lindex ${value} 1 ] ]
            }
            default {
                puts [ format " * %-25s : %s" ${name} ${value} ]
            }
        }
    }

    return

}

#- Get the list of status information
puts "HTTP client status information:"
showHttpClientInformation $httpClient $requestSize

#- Retrieve HTTP client information from the HTTP server side.
puts "HTTP server status information for the HTTP client:"
showHttpServerClientInformation $httpServer $httpClient

#
# --- Cleanup ---
#
#- REMARK: Deleting an Object will also Destruct all child Objects,
#  e.g. Layer2, Layer3, Stream, Trigger on a logical Port Objects,
#       Dhcp on the IPv4 Layer3 Objects,
#       Frames on the Stream Objects,
#       etc.
$httpClientPort Destructor
$httpServerPort Destructor


