//---------------------------------------------------------
/// SendResponse
///
/// Sends the response either over sockets or shared memory
///
/// \param requestID the requestID to send the response to
/// \param cpsMimeType the mimetype to send the response as
/// \param cpsResponse the response to send
/// \param uResponseSize the size of the response being sent
/// \param bStreaming indicates that the response it to a streaming request
///
/// \return true if the response is 'sent' correctly; false otherwise
//---------------------------------------------------------
bool SendResponse(CommunicationID requestID, const char* cpsMimeType, const char* cpsResponse, unsigned int uResponseSize, bool bStreaming)
{
    // find out if this is a streaming response
    if (bStreaming)
    {
        Log(logTRACE, "Sending response over socket\n");
        // this is a streaming response
        // use the socket for comms

        return SendMimeResponse(requestID, cpsMimeType, cpsResponse, uResponseSize);
    }

    // use Shared memory if this is not a streaming response
    if (smLockPut("PLUGINS_TO_GPS", sizeof(requestID) + ((unsigned long) strlen(cpsMimeType) * sizeof(const char)) + uResponseSize, 3) == false)
    {
        Log(logASSERT, "Not enough space in shared memory for response.\n");
        return false;
    }

    NamedSemaphore semaphore;
    bool opened = semaphore.Open("PLUGINS_TO_GPS_SEMAPHORE");

    if (opened)
    {
        if (semaphore.Signal() == false)
        {
            Log(logWARNING, "Failed to signal PLUGINS_TO_GPS_SEMAPHORE. Response may be lost. Error is %d, Previous count is 0\n", osGetLastSystemError());
        }

        semaphore.Close();
    }
    else
    {
        Log(logWARNING, "Failed to open PLUGINS_TO_GPS_SEMAPHORE. Response may be delayed.\n");
    }

    bool bResult = (smPut("PLUGINS_TO_GPS", &requestID, sizeof(requestID)) &&
                    smPut("PLUGINS_TO_GPS", (void*)cpsMimeType, (unsigned long) strlen(cpsMimeType) * sizeof(const char)) &&
                    smPut("PLUGINS_TO_GPS", (void*)cpsResponse, uResponseSize));

    smUnlockPut("PLUGINS_TO_GPS");

    if (bResult == false)
    {
        Log(logASSERT, "Failed to put part of the response into shared memory\n");
    }
    else
    {
        // remove the request
        RemoveRequest(requestID);
    }

    return bResult;
}
//--------------------------------------------------------------
/// Create the shared memory needed to communicate between the
/// web server and the plugin
/// \return true if successful, false if error
//--------------------------------------------------------------
bool ProcessTracker::CreateSharedMemory()
{
#ifdef _WIN32

    if (smExists("GPS_TO_MDLL"))
    {
        if (smOpen("GPS_TO_MDLL"))
        {
            if (smLockGet("GPS_TO_MDLL"))
            {
                //the shared memory already exists, so we need to read everything from it, to make sure it's empty
                // closing and recreated the shared memory does not work because the MicroDLL has it open
                // so that it can inject into any spawned processes (via CreateProcess). If MicroDLL doesn't keep
                // a handle to the shared memory, it could be closed by everyone, get destroyed and then it wouldn't
                // be able to follow CreateProcess.
                char tmp[ PS_MAX_PATH ];

                while (smGet("GPS_TO_MDLL", NULL, 0) != 0)
                {
                    // the smGet call will clear the shared memory
                    smGet("GPS_TO_MDLL", tmp, PS_MAX_PATH);
                }

                smUnlockGet("GPS_TO_MDLL");
            }
        }
    }

    // now write the plugin info into the shared memory
    // Put wrapper info into a shared memory for micro DLL
    // make the shared memory big enough for all the wrappers that are known about
    // even though we're only going to write in the wrappers that are allowed
    if (smCreate("GPS_TO_MDLL", (unsigned long)GetWrapperMap().size() * 3, PS_MAX_PATH))
    {
        // lock the buffer if it has enough space for 3 strings for each of the allowed wrappers
        unsigned long ulNumBuffers = (unsigned long) g_allowedWrappersMap.size() * 3;

        if (smLockPut("GPS_TO_MDLL", ulNumBuffers * PS_MAX_PATH, ulNumBuffers))
        {
            char strPath[PS_MAX_PATH];
            char strName[PS_MAX_PATH];
            char strDlls[PS_MAX_PATH];

            for (WrapperMap::const_iterator iter = g_allowedWrappersMap.begin();
                 iter != g_allowedWrappersMap.end();
                 ++iter)
            {
                strcpy_s(strPath, PS_MAX_PATH, iter->second.strPluginPath.asCharArray());
                strcpy_s(strName, PS_MAX_PATH, iter->second.strPluginName.asCharArray());
                strcpy_s(strDlls, PS_MAX_PATH, iter->second.strWrappedDll.asCharArray());

                if (smPut("GPS_TO_MDLL", (void*) strPath, PS_MAX_PATH) == false ||
                    smPut("GPS_TO_MDLL", (void*) strName, PS_MAX_PATH) == false ||
                    smPut("GPS_TO_MDLL", (void*) strDlls, PS_MAX_PATH) == false)
                {
                    Log(logERROR, "Couldn't put wrapper info into shared memory.\n");
                    smUnlockPut("GPS_TO_MDLL");
                    return false;
                }
            }

            smUnlockPut("GPS_TO_MDLL");
        }
        else
        {
            Log(logERROR, "There is not enough space in the shared memory for the desired content.\n");
            return false;
        }
    }
    else
    {
        Log(logERROR, "Couldn't create shared memory to pass wrapper registration\n");
        return false;
    }

#else

    for (WrapperMap::const_iterator iter = g_allowedWrappersMap.begin();
         iter != g_allowedWrappersMap.end();
         ++iter)
    {
        // On Linux, Create the shared memory from the PerfStudio server so that
        // it is cleaned up properly.
        if (smCreate(iter->second.strPluginShortDesc.asCharArray(), 100, sizeof(HTTPRequestHeader)) == false)
        {
            Log(logERROR, "Couldn't create shared memory for plugin.\n");
            return false;
        }
    }

#endif // def _WIN32
    return true;
}
////////////////////////////////////////////////////////////////////////////////////////////
/// Passes the supplied request header to the specifed plugin
/// \param strDestination the short description of the plugin to pass the request to
/// \param pRequestHeader the request header to pass to the plugin
/// \param pid the process id to pass the request to.
/// \param pClientSocket the socket used to read the request
/// \return true if the request was passed to the plugin; false otherwise
////////////////////////////////////////////////////////////////////////////////////////////
bool ProcessTracker::PassRequestToPlugin(const char* strDestination,
                                         HTTPRequestHeader* pRequestHeader,
                                         unsigned long pid,
                                         NetSocket* pClientSocket)
{
    if (strstr(pRequestHeader->GetHeaderData()->url, STR_STREAM_TOKEN) != NULL)
    {
        Log(logTRACE, "Duplicating Socket for request: %s\n", pRequestHeader->GetHeaderData()->url);

        // this is a streaming request, duplicate the socket
        // to reduce the overhead incurred by the shared memory approach
#if defined (_WIN32)

        if (pClientSocket->DuplicateToPID(pid, &pRequestHeader->GetHeaderData()->ProtoInfo) != 0)
#else
        PS_UNREFERENCED_PARAMETER(pid);

        if (CreateStreamSocket(pRequestHeader, pClientSocket) == false)
#endif
        {
            Log(logERROR, "Failed to duplicate socket for streaming request. Error %lu\n", osGetLastSystemError());
            return false;
        }
    }

    bool bResult = false;

    if (smLockPut(strDestination, sizeof(HTTPRequestHeader), 1))
    {
        // Check to see if the postdata failed to come over in some way.
        if (pRequestHeader->GetPostDataSize() > 0 &&  pRequestHeader->GetPostData() == NULL)
        {
            Log(logERROR, "PostData size is %d, but PostData is NULL\n", pRequestHeader->GetPostDataSize());
            // Force the data size to zero
            pRequestHeader->SetPostDataSize(0);
        }

        // Create a new record of this request
        // We will check to see when it comes back from the server.
        //RequestInFlight* pNewRequest = new RequestInFlight(pRequestHeader, pClientSocket);

        // Add the new record to the DB.
        //RequestsInFlightDatabase::Instance()->Add(pClientSocket, pNewRequest);

        bResult = smPut(strDestination, pRequestHeader->GetHeaderData(), sizeof(HTTPHeaderData));

        // Keep in for debugging
        //StreamLog::Ref() << "1) smPut HTTPRequestHeader\n" ;

        // If there is POST data we must send that too.
        if (pRequestHeader->GetPostDataSize() > 0 && pRequestHeader->GetPostData() != NULL)
        {
            bResult = smPut(strDestination, pRequestHeader->GetPostData(), pRequestHeader->GetPostDataSize());

            // Keep in for debugging
            //StreamLog::Ref() << "2) smPut Shader Code: " << pRequestHeader->pPostData << "\n";
        }

        smUnlockPut(strDestination);
#ifdef DEBUG_COMMS_PERFORMANCE
        CommandTimingManager::Instance()->IncrementServerLoadingCount(1) ;
#endif
    }

#ifdef DEBUG_COMMS_PERFORMANCE
    // Record the time now that we have sent the command over the shared memory.
    CommandTiming* cmdTiming = CommandTimingManager::Instance()->GetTimingFromPendingList(pClientSocket);

    if (cmdTiming != NULL)
    {
        LARGE_INTEGER nPerformanceCount;
        QueryPerformanceCounter(&nPerformanceCount);
        cmdTiming->SetPostSharedMemorySend(nPerformanceCount);
        // Set the current server loading value
        cmdTiming->SetServerLoadingCount(CommandTimingManager::Instance()->GetServerLoadingCount());
    }

#endif

    return bResult;
}