#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;
}