host/cxpslib/requesthandler.h (582 lines of code) (raw):
///
/// \file requesthandler.h
///
/// \brief handles requests
///
#ifndef REQUESTHANDLER_H
#define REQUESTHANDLER_H
#include <string>
#include <map>
#include <vector>
#include <cstddef>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include "zlib.h"
#include "tagvalue.h"
#include "protocoltraits.h"
#include "connection.h"
#include "authentication.h"
#include "errorexception.h"
#include "listfile.h"
#include "wallclocktimer.h"
#include "cxpslogger.h"
#include "serveroptions.h"
#include "zflate.h"
#include "fio.h"
#include "cxps.h"
#include "responsecode.h"
#include "ThrottlingHelper.h"
#include "DiffResyncThrottlingHelper.h"
#include "Telemetry/RequestTelemetryData.h"
using namespace CxpsTelemetry;
/// \brief holds request information
struct RequestInfo {
// if this ever fails to compile consult boost.function documentation
// about possible work arounds for the failing compiler
typedef boost::function<void ()> completedCallback_t; ///< type for request completed callback function
/// \brief constructor
RequestInfo() {}
/// \brief constructor
RequestInfo(std::string request, ///< request to process
tagValue_t & params, ///< request parameters
std::size_t dataSize, ///< total size of data being sent with the request
std::size_t bufferLen, ///< legnth of data in buffer that still needs to be processed
char* buffer, ///< points to buffer holding data that has already been read
bool ssl, ///< indicates if the request is using ssl true: yes, false: no
completedCallback_t completedCallback) ///< function to call when the request has been processed
: m_request(request),
m_params(params),
m_dataSize(dataSize),
m_bufferLen(bufferLen),
m_buffer(buffer),
m_ssl(ssl),
m_completedCallback(completedCallback)
{}
void set(std::string request, ///< request to process
tagValue_t & params, ///< request parameters
std::size_t dataSize, ///< total size of data being sent with the request
std::size_t bufferLen, ///< legnth of data in buffer that has already been read
char* buffer, ///< points to buffer holding data that has already been read
bool ssl, ///< indicates if the request is using ssl true: yes, false: no
completedCallback_t completedCallback ///< function to call when the request has been processed
)
{
m_request = request;
m_params = params;
m_dataSize = dataSize;
m_bufferLen = bufferLen;
m_buffer = buffer;
m_ssl = ssl;
m_completedCallback = completedCallback;
}
void reset()
{
m_request.clear();
m_params.clear();
m_dataSize = 0;
m_bufferLen = 0;
m_buffer = 0;
}
std::string m_request; ///< request made
tagValue_t m_params; ///< holds request params
std::size_t m_dataSize; ///< total size of data being sent with the request
std::size_t m_bufferLen; ///< size of data in buffer that still needs to be processed
char* m_buffer; ///< holds data that was read and still needs to be processed
bool m_ssl; ///< indicates if the request is using ssl true: yes, false: no
completedCallback_t m_completedCallback; ///< fucntion to be called if the request is successful
};
/// \brief does the actual request processing
class RequestHandler : public boost::enable_shared_from_this<RequestHandler> {
public:
// if this ever fails to compile consult boost.function documentation
// about possible work arounds for the failing compiler
#define MAKE_PROCESS_REPLY_CALLBACK_MEM_FUN(processReplyMemberFunction, processReplyPtrToObj) boost::bind(processReplyMemberFunction, processReplyPtrToObj, _1, _2)
typedef boost::function<void (bool, std::string)> processReplyCallback_t; ///< process reply bool: success or fail, std::string content (can be empty)
typedef std::map<std::string, ConnectionAbc::ptr> cfsConnections_t; ///< type for hold cfs connections
typedef boost::shared_ptr<RequestHandler> ptr; ///< requestHandler pointer type
/// \brief constructor
explicit RequestHandler(ConnectionAbc::ptr connection, ///< connecto to use to read/write data
std::string const& sessionId, ///< sesion id associated with the request handler
boost::asio::io_service& ioService, ///< io service to use
serverOptionsPtr serverOptions, ///< determines if uploaded diff files should be compressed
HttpProtocolHandler* sessionProtocolHandler,
CxpsTelemetry::RequestTelemetryData& requestTelemetryData
);
/// \brief constructor used when going from a client to server as done by CfsControlClient
RequestHandler(ConnectionAbc::ptr connection,
std::string const& sessionId,
boost::asio::io_service& ioService,
serverOptionsPtr serverOptions,
std::string const& snonce,
boost::uint32_t reqId,
std::string const& cnonce,
std::string const& hostId,
HttpProtocolHandler* sessionProtocolHandler,
CxpsTelemetry::RequestTelemetryData& requestTelemetryData
);
~RequestHandler();
/// \brief returns the session id
std::string sessionId()
{
return m_sessionId;
}
/// \brief returnes the host id that logged in
const std::string& peerHostId()
{
return m_hostId;
}
/// \brief function called to process the request
void process(HttpTraits::reply_t::ptr reply); ///< pointer to reply used to send reply
/// \brief logs xfer failed
void logXferFailed(char const* reason); ///< text of what failed
/// \brief track async transfer time
void timeStart();
/// \brief end track async transfer time
void timeStop();
/// \brief track file IO time
void timeFileIoStart();
/// \brief end track file IO time
void timeFileIoStop();
/// \brief logs session out of server
void sessionLogout();
/// \brief set request info
void setRequestInfo(std::string request, ///< request to process
tagValue_t & params, ///< request parameters
std::size_t dataSize, ///< total size of data being sent with the request
std::size_t bufferLen, ///< legnth of data in buffer that has already been read
char* buffer, ///< points to buffer holding data that has already been read
bool ssl, ///< indicates if the request is using ssl true: yes, false: no
RequestInfo::completedCallback_t completedCallback) ///< function to call when the request has been processed
{
m_requestInfo.set(request, params, dataSize, bufferLen, buffer, ssl, completedCallback);
}
/// \brief gets the current request info (if any) as a printable string
std::string getRequestInfoAsString(bool logAdditionalInfo = true); ///< determines if additional request info should be included. true: yes, false: no
/// \brief checks if passed in file is open
///
/// \return
/// \li \c true : file is open
/// \li \c false: file is not open or not being used by this session
std::string isFileOpen(boost::filesystem::path const& fileName, ///< file name to check
bool forceClose, ///< close file if true
bool checkGetFile ///< check getfile if true
)
{
// isFileOpen() is called from other session/thread; acquire the lock to avoid seg fault.
// This serializes access from other session to m_putFileInfo.m_name and m_getFileInfo.m_name
// no need to serialize access within the thread/session that owns these data
boost::mutex::scoped_lock guard(m_interSessionCommunicationMutex);
if (m_putFileInfo.m_name.string() == fileName.string() && m_putFileInfo.m_fio.is_open()) {
if (forceClose) {
m_putFileInfo.m_fio.close();
}
return m_sessionId;
}
if (checkGetFile && m_getFileInfo.m_name.string() == fileName.string() && m_getFileInfo.m_fio.is_open()) {
if (forceClose) {
m_getFileInfo.m_fio.close();
}
return m_sessionId;
}
return std::string();
}
/// \brief cancels a timeout that has been set
void cancelTimeout() {
boost::system::error_code ec;
m_timer.cancel(ec);
}
/// \brief checks if async request is queued
///
/// \return bool true: queued, false: not queued
bool isQueued()
{
return (m_asyncQueued || m_timeoutQueued);
}
/// \brief sends a cfs connect back request to cxps
bool sendCfsConnectBack(std::string const& cfsSessionId, ///< session id making the request, this is sent with the request
bool secure); ///< indicates if the connect back should be secure true: yes, false: no this is sent with the request
/// \brief sends cfs heartbeat to prevent cfs control channel from closing during long periods of inactivity
bool sendCfsHeartbeat();
protected:
/// \brief initializes request handler
void init();
// if this ever fails to compile consult boost.function documentation
// about possible work arounds for the failing compiler
typedef boost::function<void ()> action_t; ///< action to take for a given request
typedef std::map<std::string, action_t> request_t; ///< maps a request with its action
/// \brief log request optionally additional request info
void logRequest(int level, ///< log level
char const* stage, ///< stage of the request (received, started, done)
bool logAdditionalInfo ///< determines if additional request info should be logged true: yes false: no
);
/// \brief logging received requests
void logRequestReceived() {
logRequest(MONITOR_LOG_LEVEL_2, "RECEIVED", false);
}
/// \brief logging failed requests
void logRequestNotFound() {
logRequest(MONITOR_LOG_LEVEL_2, "NOT FOUND", true);
}
/// \brief logging failed requests
void logRequestFailed() {
logRequest(MONITOR_LOG_LEVEL_2, "FAILED", true);
}
/// \brief log the start of a request
void logRequestBegin() {
logRequest(MONITOR_LOG_LEVEL_2, "BEGIN", true);
}
/// \brief to log when async handling of a request does processing
void logRequestProcessing() {
logRequest(MONITOR_LOG_LEVEL_3, "PROCESSING", true);
}
/// \brief to log when a request is done
void logRequestDone() {
logRequest(MONITOR_LOG_LEVEL_2, "DONE", true);
}
/// \brief log request with additional information when it is done
void logRequestDone(const char* str) ///< addtional information to log
{
std::string msg("DONE\t");
msg += str;
logRequest(MONITOR_LOG_LEVEL_2, msg.c_str(), true);
}
/// \brief registers supported requests and their actions
void registerRequests();
/// \brief processes login request
void login();
/// \brief processes specific login request
void login(std::string const& httpRequest); ///< actual login request (/cfslogin or /login)
/// \brief processes logout request
void logout();
/// \brief processes get file request
void getFile();
/// \brief deletes the "put file" if it is not successfully received
void deletePutFile();
/// \brief parse putfile params
///
/// \note putfile parms are
/// \n\n
/// moredata=more&offset=fileoffset&name=filename&data=file data to write
/// \n\n
/// offset is optional and data= must be the last parameter
bool parsePutFileParams(boost::system::error_code const & error, size_t bytesTransferred
//char*& buffer, ///< set to point to buffer holding put file data that my need to be written after parsing done
//std::size_t& idx, ///< index into buffer for start of put file data that needs to be written
//std::size_t& totalBytesLeftToRead ///< set to the number of bytes left to read after parsing done
);
/// \brief gets the file data portion of a putfile request
void putFileGetData();
/// \brief processes put file request
void putFile();
/// \brief flush put file data to disk
void flushPutFileToDisk();
/// \brief used end a successful put file request
void putFileEnd();
/// \brief used to read all remaining data from the socket and send throttle message
void ReadEntireDataAndSendThrottle();
/// \brief processes delete file request
void deleteFile();
/// \brief processes rename file request
void renameFile();
/// \brief processes list file request
void listFile();
/// \brief processes heartbeat request
void heartbeat();
/// \brief processes heartbeat request
void cfsHeartbeat();
/// \brief processes heartbeat request
void heartbeat(bool cfsHeartbeat);
/// \brief special reset for putfile request
void resetPutFile();
/// \brief resets the request timer
void resetTime();
/// \brief resets request info
void resetRequestInfo();
/// \brief resets request handler
void reset();
/// \brief converts file names into their full paths
void getFullPathName(std::string const& name, ///< file name to be converted
boost::filesystem::path& fullPath, ///< recevies the full path
bool bCheckAllowedDir = true ///< perform allowed_dirs check by default
);
/// \brief converts file names into their full paths
void getFullPathNameWrapper(std::string const& name, boost::filesystem::path& fullPath);
/// \brief peforms asynchronous put file
///
/// issues an asynchronous read of the data being "put" (i.e. being sent by the requester)
void asyncPutFile();
/// \brief processes the data returned by the asyncPutFile
void handleAsyncPutFile(boost::system::error_code const & error, ///< error code indicating the async result
size_t bytesTransferred ///< number of bytes transferred
);
/// \brief issues an async read for putfile request which are throttled
void ReadEntireDataFromSocketAsync();
///\brief reads data from client asynchronously
void handleReadEntireDataFromSocketAsync(boost::system::error_code const & error, ///< error code indicating the async result
size_t bytesTransferred ///< number of bytes transferred
);
/// \brief peforms asynchronous get file
///
/// issues an asynchronous write of the "get" data (i.e. being sent back to the requester)
void asyncGetFile(char const* buffer, ///< buffer to write
std::size_t size); ///< size (in bytes) of data in buffer
/// \brief process the results of asyncGetFile
void handleAsyncGetFile(boost::system::error_code const & error, ///< error code indicating the async result
size_t bytesTransferred ///< number of bytes transferred
);
/// \brief sends bad request reply
///
void badRequest(char const* loc, ///< file location where the bad request was detected
char const* reason ///< reason text explaining why the request was bad
);
/// \brief send throttle reply
void sendThrottleError(char const* loc, ///< file location where the bad request was detected
char const* reason, ///< reason text explaining why the request was bad
const std::map<std::string, std::string>& responseHeaders ///< headers to send in response
);
/// \brief log putfile transfer info
///
/// \param status result of the put file request (success or failed)
///
/// \note outputs the following tab separated fields on a single line
/// \li Log time - time request written to xfer log
/// \li Request - /putifle
/// \li Host Id - host id of the requestor
/// \li Ip address - ip address of the requestor
/// \li File name - the name of the file where the data was written
/// \li File size - the amount of the file successfully sent
/// \li Transfer time - rough estimate of time it took to transfer data in milliseconds.
/// If all the data can fit into one transfer buffer, then transfer time will be 0.
/// \li SSL - yes: encrypted using ssl, no: no encryption
/// \li Result - success | failed
void logXferPutFile(char const* status); ///< text indicating the status (typically success or fail)
/// \brief log getfile transfer info
///
/// \param status result of the get file request (success or failed)
///
/// \note oputputs the following tab separated fields on a single line
/// \li Log time - time request written to xfer log
/// \li Request - /getfile
/// \li Host Id - host id of the requestor
/// \li Ip address - ip address of the requestor
/// \li File name - the name of the file that was retrieved
/// \li File size - the amount of the file successfully sent
/// \li Transfer time - rough estimate of time it took to transfer data in milliseconds.
/// If all the data can fit into one transfer buffer, then transfer time will be 0.
/// \li SSL - yes: encrypted using ssl, no: no encryption
/// \li Result - success | failed
void logXferGetFile(char const* status); ///< text indicating the status (typically success or fail)
/// \brief log getfile transfer info
///
/// \param status result of the get file request (success or failed)
///
/// \note oputputs the following tab separated fields on a single line
/// \li Log time - time request written to xfer log
/// \li Request - /deletefile
/// \li Host Id - host id of the requestor
/// \li Ip address - ip address of the requestor
/// \li File name - the name of the file(s) to be deleted. can be more then one
/// \li File spec - the file spec used to as part of the file name(s) to be deleted. can be empty
/// \li Mode - mode used for delete. see finddelete.h for full details about mode.
/// basically determines if only files and/or directories and subdirectories should be deleted
/// \li SSL - yes: encrypted using ssl, no: no encryption
/// \li Result - success | failed
void logXferDeleteFile(std::string const& names, ///< holds names of files deleted
std::string const& fileSpec, ///< holds the filespec used to match the files to be deleted
int mode, ///< the delete mode used
char const* status); ///< text indicating the status
/// \brief sets the timer to the timeout duration
void setTimeout(int seconds = 0) ///< max secconds to wait for timer (default uses the server options settings)
{
m_timer.expires_from_now(boost::posix_time::seconds((seconds > 0 ? seconds : m_serverOptions->sessionTimeoutSeconds())));
m_timeoutQueued = true;
m_timer.async_wait(boost::bind(&RequestHandler::handleTimeout, shared_from_this(), boost::asio::placeholders::error));
}
/// \brief handles timeouts
void handleTimeout(const boost::system::error_code& error) ///< result of the async time out wait
{
try {
if (error != boost::asio::error::operation_aborted) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_TimedOut);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") "
<< m_connection->endpointInfoAsString() << ' '
<< getRequestInfoAsString()
<< " timed out (" << m_serverOptions->sessionTimeoutSeconds() << ')');
m_connection->setTimedOut();
m_connection->disconnect();
}
} catch (...) {
// nothing to do
// just preventing exceptions from being thrown
// as this can be called in an arbitrary thread
}
m_timeoutQueued = false;
}
/// \brief checks if a put file request is in progress when receiving
/// a non-putfile request
///
/// because putfile might need more then 1 putfile request to send all the
/// data (this avoids the need to pre-caclulate the total putfile size,
/// before starting the send), must make sure that the putfile was completely
/// sent before starting any other requests. thus once a putfile starts it
/// must receive a putfile request indicating no more data needs to be sent
/// before it is considered finished and a new request can be sent
///
/// \return
/// \li \c true: put file is in progress
/// \li \c false: put file not in progress
bool putFileInProgress(const char* request); ///< the request that is checking if a putfile is in progress
/// \brief send a succes reply
///
/// use when no additional response data needs to be sent
virtual void sendSuccess()
{
sendSuccess(0, 0, false, 0);
}
/// \brief send a succes reply with data
///
/// use when all the data can be sent in a single call
virtual void sendSuccess(char const* data, ///< points to buffer holding data to send (must be at least length bytes)
size_t length) ///< length of data to send
{
sendSuccess(data, length, false, length);
}
/// \brief send a succes reply with data that will need multiple calls
///
/// use directly when extra data is needed and it requires multiple calls to send all the data,
/// otherwise use one of the othere sendSuccess APIs
virtual void sendSuccess(char const* data, ///< pointer to buffer to send (must be at least length bytes)
std::size_t length, ///< length of buffer pointed to by data
bool moreData, ///< indicates if there is more data to be sent true: yes, false: no
long long totalLength) ///< total length to be sent. (should be > length if moreData true).
{
BOOST_ASSERT(!m_requestTelemetryData.HasRespondedSuccess());
BOOST_ASSERT(m_requestTelemetryData.GetRequestFailure() == RequestFailure_Success);
try {
m_reply->sendSuccess(data, length, moreData, totalLength);
if(!moreData) // == true, during GetFile communication
m_requestTelemetryData.SuccessfullyResponded();
} catch (std::exception const& e) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": " << e.what());
} catch (...) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": unknown exception");
}
}
/// \brief send error reply
void sendError(ResponseCode::Codes result, ///< error result
char const* data, ///< points to buffer holding error data to be sent (must be at least length bytes)
std::size_t length) ///< length of data
{
BOOST_ASSERT(!m_requestTelemetryData.HasRespondedSuccess());
BOOST_ASSERT(m_requestTelemetryData.GetRequestFailure() != RequestFailure_Success ||
result == ResponseCode::RESPONSE_NOT_FOUND); // Used with Get, List and Ren.
try {
if (0 != m_reply.get()) { // NOTE should not be needed, just being cautious
m_reply->sendError(result, data, length);
}
} catch (std::exception const& e) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": " << e.what());
} catch (...) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": unknown exception");
}
}
/// \brief send error reply
void sendThrottleError(ResponseCode::Codes result, ///< error result
char const* data, ///< points to buffer holding error data to be sent (must be at least length bytes)
std::size_t length, ///< length of data
const std::map<std::string, std::string>& responseHeaders ///< headers to send in response
)
{
BOOST_ASSERT(!m_requestTelemetryData.HasRespondedSuccess());
BOOST_ASSERT(m_requestTelemetryData.GetRequestFailure() != RequestFailure_Success);
try {
if (0 != m_reply.get()) { // NOTE should not be needed, just being cautious
m_reply->sendError(result, data, length, responseHeaders);
// To do: sadewang: This is not a success case, since the file has not been written to disk
// so mark it as failure case in telemetry and process telemetry stats accordingly.
m_requestTelemetryData.SuccessfullyResponded();
}
}
catch (std::exception const& e) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": " << e.what());
}
catch (...) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_ErrorInResponse);
CXPS_LOG_ERROR(AT_LOC << "(sid: " << m_sessionId << ") " << m_connection->endpointInfoAsString() << ": unknown exception");
}
}
/// \brief checks if the file should be compressed
///
/// \return
/// \li true: compress
/// \li false: no not compress
bool compressFile(boost::filesystem::path const& name); ///< name of file to check
/// \brief determines if the name meets the criteria of a file that is OK to compress
///
/// \return
/// \li true: OK to compress file
/// \li false: not OK to compress file
bool isCompressableDiffFile(boost::filesystem::path const& name) ///< name of file to check
{
return ((std::string::npos != name.string().find("diff_") || std::string::npos != name.string().find("sync_"))
&& name.extension() == ".dat"
&& std::string::npos == name.string().find("missing"));
}
/// \brief writes put file data
///
/// \note checks if the data should be compressed, if so it will compress it before writing
long writePutFileData(char* data, ///< data to write
long dataSize); ///< size in bytes of data to write
/// \brief close file callback that always forces the file to be closed
void closeFileCallback(boost::filesystem::path const& name); ///< name of file to close
/// \brief updates the rpo monitor txt with the last received diff file info
void updateRpoMonitor(extendedLengthPath_t const& name); ///< name of rpo monitor file
/// \brief checks if the request is embedded in the request data
///
/// this should not longer be needed as this issue was fixed by using
/// 2 separate writes when sending the request and data instead of combining
/// them into a single buffer.
void checkForEmbeddedRequest(char* data, ///< data to be checked
long dataSize); ///< size (in bytes) of data to be checked
/// \brief clear async queued called when an async handler is called
void clearAsyncQueued()
{
m_asyncQueued = false;
}
/// \brief checks if the file to be processed is under an allowed dir
void checkAllowedDir(std::string const& name); ///< name of file to check
/// \brief verifies if the file to be processed is under an allowed dir based on biosid and hostid mapping
/// from the PS settings in RCM mode.
void VerifyDirAccess(std::string const& fileName, boost::filesystem::path const& filePath);
/// \brief Allows access if the request contains get/list request to agent repository directory.
void ValidateAgentRepositoryDirAccess(std::string const& fileName);
/// \brief Allows access if the file to be processed is of agenthostid.mon format or
/// if the requested dir is having reqDefaultDir\tstdata\agenthostid path
void ValidateReqDefaultDirAccess(std::string const& hostId,
std::string const& fileName,
boost::filesystem::path const& filePath,
std::list<std::string> const& hostIdsWithSameBiosId);
/// \brief Sanitizes the filepath for comparision
boost::filesystem::path SanitizeFilePath(std::string const& filePath);
/// \brief Verifies if the logfile/telemetryfile to be processed is under an allowed
/// dir based on hostidtologfolder mapping or hostidtotelemetryfolder mapping from PS settings in RCM mode.
void ValidateDirAccessRetrivedFromSettings(std::string const& hostId,
std::string const& fileName,
boost::filesystem::path const& filePath,
ServerOptions::hostIdDirMap_t hostIdDirMap,
std::list<std::string> const& hostIds);
/// \brief Return access status if the file to be processed is of agenthostid.mon format or
/// if the requested dir is having reqDefaultDir\tstdata\agenthostid path
void GetReqDefaultDirAccessStatus(std::string const& hostId,
std::string const& fileName,
boost::filesystem::path const& filePath,
boost::filesystem::path& tstDataFilePath,
boost::filesystem::path& monFilePath,
bool& allowAccess);
/// \brief Return access status if the logfile/telemetryfile to be processed is under an allowed
/// dir based on hostidtologfolder mapping or hostidtotelemetryfolder mapping from PS settings in RCM mode.
bool GetCacheAndTelemetryDirAccessStatus(std::string const& hostId,
std::string const& fileName,
boost::filesystem::path const& filePath,
ServerOptions::hostIdDirMap_t hostIdDirMap);
/// \brief checks if the nonce is valid
void validateCnonce();
/// \brief process cfs login request
void cfsLogin();
/// \brief process fx login request
void fxLogin();
/// \brief process cfs connect back request
void cfsConnectBack();
/// \brief completes cfs connect request
void completeCfsConnect(std::string const& cfsSessionId); ///< cfs session id
/// \brief processes a cfs connect request
void cfsConnect();
/// \brief completes send cfs connect back operation
void completeSendCfsConnectBack(bool success, ///< indicates if request was successful or not true: success, false: fail
std::string replyData); ///< data being returned, if success true has connect back data, else has error text
/// \brief default complete cfs function when the request does not
/// require any completion processing (e.g. cfsHeartbeat)
void completeCfsDefault(bool, ///< not used
std::string) ///< not used
{
// nothing to do
}
/// \brief sends a cfs connect request
void sendCfsConnect(std::string const& cfsSessionId, ///< cfs session id to be sent with request
unsigned short cfsPort, ///< cfs port (should be ssl port if secure is yes else non ssl port)
std::string const& secure); ///< secure mode to be sent with request
/// \brief handler for aysnc write N
void handleAsyncWriteN(processReplyCallback_t processReplyCallback, ///< callback function that will process reply
boost::system::error_code const & error, ///< holds error (if any)
size_t bytesTransferred); ///< total bytes transfered to buffer
/// \brief handler for async read reply
void handleAsyncReadReply(processReplyCallback_t processReplyCallback, ///< callback function that will process reply
boost::system::error_code const& error, ///< hold error (if nay)
size_t bytesTransferred); ///< bytes transfered to buffer
void handleAsyncReadReplyData(processReplyCallback_t processReplyCallback, ///< callback function that will process reply
std::string content, ///< holds content data returned with reply (if any)
std::size_t remainingToTransfer, ///< remaining content data bytes left to be transfered
boost::system::error_code const & error, ///< hold error (if any)
size_t bytesTransferred); ///< bytes transfered to buffer
/// \brief track cfs connection
void trackCfsConnection(std::string const& sessionId, ///< cfs session id to track
ConnectionAbc::ptr connection) ///< ConnectionAbc::ptr that holds connection to be tracked
{
boost::mutex::scoped_lock guard(m_cfsConnectionMutex);
m_cfsConnections.insert(std::make_pair(sessionId, connection));
}
/// \brief stop tracking cfs connection
void stopTrackingCfsConnection(std::string const& sessionId) ///< cfs session id to stop tracking
{
boost::mutex::scoped_lock guard(m_cfsConnectionMutex);
cfsConnections_t::iterator findIter(m_cfsConnections.find(sessionId));
if (m_cfsConnections.end() != findIter) {
m_cfsConnections.erase(findIter);
}
}
/// \brief gets the version sent with the request
///
/// version 2014-08-01 will support no version number to allow
/// previous versions (that do not use version) to still work
/// after that will require version to be present (i.e. require
/// clients to upgrade once cxps is upgraded to version after 2014-08-01)
/// in general this is OK as when upgrading cxps, clients will also upgrade, just
/// that clients upgrade after cxps.
///
/// \return true: if version sent (or current version is 2014-08-01), false otherwise
bool getVersion(std::string& version); ///< set to the version number sent with request (can be empty)
/// checks if cfs mode should reject request received over non secure connection
///
/// cfs mode by default will reject all requests sent over non secure connection expcetp /cfsconnect
/// as that one is needed for cfs connection and will explicitily switch to secure if needed
///
/// \throws std:exception if non secure request should be rejected
void checkCfsNonSecureRequest(std::string const& action)
{
if (m_serverOptions->cfsMode()
&& m_serverOptions->cfsRejectNonSecureRequests()
&& !m_connection->usingSsl()
&& !boost::algorithm::equals(action, HTTP_REQUEST_CFSCONNECT)) {
m_requestTelemetryData.SetRequestFailure(RequestFailure_CfsInsecureRequest);
throw ERROR_EXCEPTION << "invalid request";
}
}
/// checks for cumulative, diff and resync throttle
/// internally calls functions for all three types of throttle
void checkForThrottle(bool enableDiffResyncThrottle, const boost::filesystem::path & fullPathName);
/// checks if cumulative throttle should be set for given putfile request
void checkForCumulativeThrottle();
/// checks if diff throttle should be set for given putfile request
void checkForDiffThrottle(const boost::filesystem::path & fullPathName);
/// checks if resync throttle should be set for given putfile request
void checkForResyncThrottle(const boost::filesystem::path & fullPathName);
/// sets the request failure in telemetry for inline throttling
void setThrottleRequestFailureInTelemetry();
private:
ConnectionAbc::ptr m_connection; ///< holds connection object
request_t m_requests; ///< holds the registered requests
std::vector<char> m_buffer; ///< buffer for reading/writing
std::string m_snonce; ///< random generated bytes
std::string m_sessionId; ///< session id for the connection
std::string m_hostId; ///< peer host id used for the login
std::string m_cnonce; ///< client sent nonce
unsigned int m_reqId; ///< request id
RequestInfo m_requestInfo; ///< current request info being processed
HttpTraits::reply_t::ptr m_reply; ///< holds reply object
boost::asio::deadline_timer m_timer; ///< for detecting timeouts
WallClockTimer m_wallClockTimer; ///< for caclulation xfer wall clock time
WallClockTimer m_wallClockTimerFileIo; ///< for caclulation xfer wall clock time for File IO
long double m_totalRequestTimeMilliSeconds; ///< holds total xfer time in milli-seconds
long double m_totalFileIoTimeMilliSeconds; ///< holds total file IO time in milli-seconds
/// \brief holds put file info
///
/// used to handle asynchronous put file
struct putFileInfo {
putFileInfo()
: m_bytesLeft(0),
m_moreData(false),
m_openFileIsNeeded(true),
m_createDirs(false),
m_totalBytesReceived(0),
m_buffer(0),
m_totalBytesLeftToRead(0),
m_idx(0),
m_bytesProcessed(0),
m_bytesChecked(0),
m_readingToken(true),
m_haveMoreDataFlagOld(false),
m_haveFileNameOld(false)
{
resetThrottlingParams();
}
/// \brief reset internal state
void reset() {
m_bytesLeft = 0;
m_sentName.clear();
m_name = ""; // looks like path is missing clear(), but docs say it is there
m_moreData = false;
if (m_fio.is_open()) {
m_zFlate.reset((Zflate*)0);
m_fio.close();
}
m_fio.clear();
m_openFileIsNeeded = true;
m_createDirs = false;
m_totalBytesReceived = 0;
resetParseParams();
resetThrottlingParams();
}
void resetParseParams()
{
m_buffer = 0;
m_totalBytesLeftToRead = 0;
m_idx = 0;
m_token.clear();
m_value.clear();
m_bytesProcessed = 0;
m_bytesChecked = 0;
m_readingToken = true;
// old parse
m_fileNameOld.clear();
m_haveMoreDataFlagOld = false;
m_haveFileNameOld = false;
m_filetype = FileType_Unknown;
m_deviceId.clear();
}
// this should be called in each putfile request as throttling
// information is not carried over across requests
void resetThrottlingParams()
{
m_isCumulativeThrottled = false;
m_isDiffThrottled = false;
m_isResyncThrottled = false;
}
std::string m_sentName; ///< name as sent by requester
size_t m_bytesLeft; ///< bytes left to read for this request
boost::filesystem::path m_name; ///< full put file name
bool m_moreData; ///< more data to be sent in another request
FIO::Fio m_fio; ///< put file
bool m_compress; ///< should the data be compressed before being written to disk true: yes, false: no
boost::shared_ptr<Zflate> m_zFlate; ///< Zflate used to do the compressing
bool m_openFileIsNeeded; ///< tracks if the putFile needed to be open when the request was received true: yes, false: no
bool m_createDirs; ///< indicates if missing dirs should be created (true: yes, false: no)
unsigned long long m_totalBytesReceived; ///< tracks total bytes transfered for the putRequest
std::string m_deviceId; ///< the device id/ disk id for the request sent
CxpsTelemetry::FileType m_filetype; ///< the type of file sent in the request (For the list of filetypes, refer enum FileType in file TelemetrySharedParams.h)
// for parsing putfile post parameters
char* m_buffer; ///< buffer used for parsing putfile post parameters
std::size_t m_totalBytesLeftToRead; ///< total bytes left in the buffer
std::size_t m_idx; ///< current position with in the buffer
std::string m_token; ///< holds the post parameter name
std::string m_value; ///< holds the post parameter value for the name
std::size_t m_bytesProcessed; ///< number of bytes already processed while parsing post parameters
int m_bytesChecked; ///< total number of bytes checked looking for valid post paremeters
bool m_readingToken; ///< indicates if currently reading the post parameter token or the value (true: token, false: value)
// for parsing old putfile post parameters
std::string m_fileNameOld; ///< old putfile post param: holds the filename value
bool m_haveMoreDataFlagOld; ///< old putfile post param: indicates if moredata flag has been fully read (true: yes, false: no)
bool m_haveFileNameOld; ///< old putfile post param: indicates if the filename has been fully read (true: yes, false: no)
// for putfile throttling
bool m_isCumulativeThrottled; ///< flag to check if the putfile request is cumulative throttled
bool m_isDiffThrottled; ///< flag to check if putfile request is diff throttled
bool m_isResyncThrottled; ///< flag to check if putfile request is resync throttled
} m_putFileInfo;
/// \brief holds get file info
///
/// used to handle asynchronous get file
struct getFileInfo {
getFileInfo()
: m_totalSize(0), ///< total size of the get file
m_totalBytesSent(0) ///< total number of bytes sent
{}
/// \brief rests internal state
void reset() {
m_name = ""; // looks like boost path is missing clear(), but docs say it is there
if (m_fio.is_open()) {
m_fio.close();
}
m_fio.clear();
m_totalSize = 0;
m_totalBytesSent = 0;
}
boost::filesystem::path m_name; ///< get file name
long long m_totalSize; ///< total size of the get file
long m_totalBytesSent; ///< total bytes sent
FIO::Fio m_fio; ///< get file
} m_getFileInfo;
serverOptionsPtr m_serverOptions; ///< server options
bool m_loggedIn; ///< indicates if logged in true: yes, false: no
int m_writeMode; ///< holds the write mode used for putfile (WRITE_MODE_NORMAL: buffered I/O system does flush, WRITE_MODE_FLUSH: buffered I/O cxps does flush)
bool m_checkForEmbeddedRequest; ///< indicates checking for embedded request should be done true: check, false: no check
bool m_asyncQueued; ///< indicates if async request is queued true: yes, false: no
bool m_timeoutQueued; ///< indicates if a timeout has been queued true: yes, false: no
boost::mutex m_interSessionCommunicationMutex; ///< for serializing access to session's data during inter session communication
long m_cnonceDurationSeconds; ///< holds time in seconds how long a cnonce is valid.
HttpProtocolHandler* m_sessionProtocolHandler; ///< pointer to protocol handler
HttpProtocolHandler m_cfsProtocolHandler; ///< cfs protocol handler
boost::mutex m_cfsConnectionMutex; ///< mutex used to serialize access to m_cfsConnections
cfsConnections_t m_cfsConnections; ///< holds cfs connections being tracked
ServerOptions::dirs_t m_fxAllowedDirs; ///< holds fx jobs allowed dirs. Only filled in for fxlogin
std::string m_loginVersion; ///< holds the version used by client when it logged in
bool m_cfsConnect;
CxpsTelemetry::RequestTelemetryData& m_requestTelemetryData;
std::string m_biosId; ///< holds certificate biosid
bool m_isAccessControlEnabled;
/// \brief holds PSLogRootfolder, PS Telemetry Folder, PSRequestDefault allowed folders from registry
boost::filesystem::path m_psLogFolderPath, m_psTelFolderPath, m_psReqDefaultDir;
ServerOptions::biosIdHostIdMap_t m_biosIdHostIdMap; ///< holds biosid and hostid mapping from PS settings
ServerOptions::hostIdDirMap_t m_hostIdLogRootDirMap, m_hostIdTelemetryDirMap; ///< holds logRootFolder and telemetryFolder from PS host and telemetry settings
/// \brief read-write lock to serialize access to allowed directories settings
boost::shared_mutex m_allowedDirsSettingsMutex;
};
#endif // REQUESTHANDLER_H