HP LoadRunner Protocol SDK
Placeholder
#pragma once
#include "ApiFunctionWithNamedArgs.h"
#include "HandleDeleters.h"
#include <string>
/// HTTP GET method
class GetMethod : public CApiFunctionWithNamedArguments
{
public:
    /// initialize with requested URL
    GetMethod(const char* url);
    virtual ~GetMethod();
private:
    /// URL of the request
    const char* m_url;
    // parsed URL elements
    /// host name e.g. www.xyz.com
    std::string m_hostName;
    /// host port number
    int m_port;
    /// URL path e.g. /abc/def
    std::string m_urlPath;
    /// URL parameters e.g. ?d=p&cx=555
    std::string m_urlParameters;
    /// user name for authorized access
    std::string m_userName;
    /// user password
    std::string m_password;
    /// method initialization part
    virtual void Init();
    /// method body
    virtual void Invoke();
    /// method termination part
    virtual void Terminate();
    /// abortion handler
    virtual void Abort();
    /// split URL into components
    void CrackUrl();
    /// create session object
    UniqueHandle CreateSession();
    /// establish connection to server
    UniqueHandle EstablishConnection(UniqueHandle &session);
    /// create HTTP request
    UniqueHandle CreateRequest(UniqueHandle &connection);
    /// receive HTTP response
    std::string ReceiveResponse(UniqueHandle& request);
    /// emulate pause for reading web page
    void EmulateUserDelayAfterResponse(int delayAfterResponseSec);
    /// save HTTP body to a file
    void SavePage(const std::string& content);
};

 
#include "GetMethod.h"
#include "RequestHeaders.h"
#include "TWEB_res.h"
#include <ProtocolExtension.h>
#include <ProtocolException.h>
#include <vector>
#include <cstdlib>
#include <fstream>

GetMethod::GetMethod(const char* url) :
    m_url(url)
{
}
GetMethod::~GetMethod()
{
}
void GetMethod::Init()
{
    // abort if the URL is null
    if (!m_url)
    {
        throw ExceptionObject(ABORT_NO_MATTER_WHAT, TWEB_NO_URL);
    }
   
    // define optional parameters
    // don't save page by default
    DefineBooleanArgument("SavePage", true, false);
    DefineNumericArgument("DelayAfterResponse", true);
    // use TinyWeb user agent by default
    DefineStringArgument("UserAgent", true, "TinyWeb");
    DefineStringArgument("SaveToParam", true);
    CrackUrl();
}
void GetMethod::Invoke()
{
    using std::string;
    try
    {
        // perform HTTP GET request
        auto session = CreateSession();
        auto connection = EstablishConnection(session);
        auto request = CreateRequest(connection);
        auto result = ReceiveResponse(request);
        // emulate user delay if needed
        int delayAfterResponseSec = static_cast<int>(GetNumericArgumentValue("DelayAfterResponse"));
        if (delayAfterResponseSec != 0)
        {
            EmulateUserDelayAfterResponse(delayAfterResponseSec);
        }
        // save page if enabled
        if (GetBooleanArgumentValue("SavePage"))
        {
            SavePage(result);
        }
        // save to parameter
        string paramName = GetStringArgumentValue("SaveToParam");
        if (!paramName.empty())
        {
            CProtocolExtension::Instance()->SaveStringToParameter(result.c_str(), paramName.c_str());
        }
    }
    catch (const CProtocolException&)
    {
        // rethrow protocol exceptions
        throw;
    }
    catch (const std::exception& ex)
    {
        // handle other exception
        throw ExceptionObject(ABORT_NO_MATTER_WHAT, TWEB_GET_FAILED, ex.what());
    }
}
void GetMethod::Terminate()
{
}
void GetMethod::Abort()
{
}
void GetMethod::CrackUrl()
{
    using std::string;
    // evaluate parameterized URL e.g. http://{subdomain}.aaa.com -> http://br.aaa.com
    string url = CProtocolExtension::Instance()->EvaluateString(m_url);
    URL_COMPONENTS components = { sizeof(URL_COMPONENTS) };
    // mark the components for extraction
    components.dwHostNameLength = 1;
    components.dwUrlPathLength = 1;
    components.dwExtraInfoLength = 1;
    components.dwUserNameLength = 1;
    components.dwPasswordLength = 1;
    if (!::InternetCrackUrl(url.c_str(), 0, 0, &components))
    {
        throw ExceptionObject(ABORT_NO_MATTER_WHAT, TWEB_INVALID_URL, GetLastError());
    }
    // abort if the scheme is not HTTP
    if (components.nScheme != INTERNET_SCHEME_HTTP)
    {
        throw ExceptionObject(ABORT_NO_MATTER_WHAT, TWEB_ONLY_HTTP_SUPPORTED);
    }
    // host name should be specified
    if (components.dwHostNameLength == 0)
    {
        throw ExceptionObject(ABORT_NO_MATTER_WHAT, TWEB_NO_HOSTNAME);
    }
    // save host name and port
    m_hostName = string(components.lpszHostName, components.dwHostNameLength);
    m_port = components.nPort;
    // save URL path
    if (components.dwUrlPathLength != 0)
    {
        m_urlPath = string(components.lpszUrlPath, components.dwUrlPathLength);
    }
    // save URL parameters
    if (components.dwExtraInfoLength != 0)
    {
        m_urlParameters = string(components.lpszExtraInfo, components.dwExtraInfoLength);
    }
    // save user name
    if (components.dwUserNameLength != 0)
    {
        m_userName = string(components.lpszUserName, components.dwUserNameLength);
    }
    // save user password
    if (components.dwPasswordLength != 0)
    {
        m_password = string(components.lpszPassword, components.dwPasswordLength);
    }
}
void GetMethod::EmulateUserDelayAfterResponse(int delayAfterResponseSec)
{
    auto start = CProtocolExtension::Instance()->GetNumberOfMillisecondsFromStartOfRun();
    // sleep for specified number of seconds
    ::Sleep(delayAfterResponseSec * 1000);
    // add the delay to the Think Time counter
    ThinkTime(delayAfterResponseSec);
    // log real delay duration in milliseconds
    int passed = CProtocolExtension::Instance()->GetNumberOfMillisecondsFromStartOfRun() - start;
    CProtocolExtension::Instance()->LogNotifyMessage(EXTENDED_ADVANCED_TRACE_LOG_LEVEL, TWEB_DELAY_DURATION, passed);
}
namespace
{
    // simple page file name generator
    std::string GenerateFileName()
    {
        // create GUID
        GUID guid = {};
        ::CoCreateGuid(&guid);
        // convert GUID to string
        OLECHAR* guidString = nullptr;
        ::StringFromCLSID(guid, &guidString);
        std::wstring name = guidString;
        // free GUID
        ::CoTaskMemFree(guidString);
        // convert to ASCII and add file extenstion
        return std::string(begin(name), end(name)) + ".html";
    }
}

void GetMethod::SavePage(const std::string& content)
{
    using namespace std;
    // remember time of page saving start
    auto start = CProtocolExtension::Instance()->GetNumberOfMillisecondsFromStartOfRun();
    // get the script directory
    string scriptPath = CProtocolExtension::Instance()->GetConfigurationAttribute(SCRIPT_DIRECTORY_ATTRIBUTE);
    auto filePath = scriptPath + "\\" + GenerateFileName();
    // open page file
    ofstream pageFile(filePath.c_str());
    if (!pageFile)
    {
        // abort current method execution but not the script
        throw ExceptionObject(ABORT_BY_CONTINUE_ON_ERROR, TWEB_FILE_OPEN_FAILED, filePath.c_str(), GetLastError());
    }
    // save page content to the file
    pageFile << content;
    pageFile.close();
    // measure time wasted for saving page to disk
    int timeWastedOnSavingPage = CProtocolExtension::Instance()->GetNumberOfMillisecondsFromStartOfRun() - start;
    // add wasted time to the Wasted Time counter
    WastedTime(timeWastedOnSavingPage);
}
std::string GetMethod::ReceiveResponse(UniqueHandle &request)
{
    // allocate buffer
    std::vector<char> buffer(1025);
    DWORD bytesRead = 0;
    std::string result;
    while (::InternetReadFile(request.get(), &buffer[0], sizeof(buffer) - 1, &bytesRead) && bytesRead)
    {
        buffer[bytesRead] = 0;
        // concatenate buffer to the resulting string
        result += buffer.data();
        bytesRead = 0;
    }
    // output non-localized text
    CProtocolExtension::Instance()->LogTextMessage(EXTENDED_DATA_RETURNED_BY_SERVER_LOG_LEVEL, "Response:\n\n");
    // output possibly binary buffer (non-printable characters are escaped)
    CProtocolExtension::Instance()->LogBuffer(EXTENDED_DATA_RETURNED_BY_SERVER_LOG_LEVEL, result.c_str(), result.length());
    // create data point "Bytes Received" fro the Analysis
    CProtocolExtension::Instance()->CreateDataPoint("BytesReceived", result.length());
    // set the message on the Vugen Result View
    SetResultMessage(TWEB_GET_URL, m_url);
    return result;
}
UniqueHandle GetMethod::CreateRequest(UniqueHandle &connection)
{
    // prepare request object (path + GET parameters)
    std::string object = m_urlPath + m_urlParameters;
    // open HTTP GET request
    UniqueHandle request(HttpOpenRequest(connection.get(), "GET", object.c_str(), 0, 0, 0, INTERNET_FLAG_RELOAD, 0), InternetHandleDeleter());
    if (!request.get())
    {
        throw ExceptionObject(ABORT_BY_CONTINUE_ON_ERROR, TWEB_REQUEST_CREATION_FAILED, GetLastError());
    }
    // set mandatory headers
    std::string headers = "Content-Type: text/html\n";
    // get custom headers from VUser data
    RequestHeaders* customHeaders = reinterpret_cast<RequestHeaders*>(CProtocolExtension::Instance()->GetVirtualUserData());
    // concatenate headers
    headers += customHeaders->GlueTogether();
    // send request to server
    if (!HttpSendRequest(request.get(), headers.c_str(), headers.length(), nullptr, 0))
    {
        throw ExceptionObject(ABORT_BY_CONTINUE_ON_ERROR, TWEB_REQUEST_SENDING_FAILED, GetLastError());
    }
    return request;
}
UniqueHandle GetMethod::CreateSession()
{
    // get user agent optional parameter
    // if not set, the default value is used
    std::string agent = GetStringArgumentValue("UserAgent");
    // open session
    UniqueHandle session(InternetOpen(agent.c_str(), INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0), InternetHandleDeleter());
    if (!session.get())
    {
        throw ExceptionObject(ABORT_BY_CONTINUE_ON_ERROR, TWEB_OPEN_SESSION_FAILED, GetLastError());
    }
    return session;
}
UniqueHandle GetMethod::EstablishConnection(UniqueHandle &session)
{
    // connect to the web server
    UniqueHandle connection(InternetConnect(
        session.get(),
        m_hostName.c_str(),
        m_port,
        m_userName.length() ? m_userName.c_str() : nullptr,
        m_password.length() ? m_password.c_str() : nullptr,
        INTERNET_SERVICE_HTTP, 0, 0),
        InternetHandleDeleter());
    if (!connection.get())
    {
        throw ExceptionObject(ABORT_BY_CONTINUE_ON_ERROR, TWEB_CONNECTION_FAILED, m_hostName.c_str(), GetLastError());
    }
    return connection;
}