RequestMap bridge::InterfaceManager::generateDifferentialRequests(bool killSwitchActive) { RequestMap diff; for (auto &newRequest : *(interface->getRequestMap())) { if (previousRequests.find(newRequest.first) == previousRequests.end()) { logger.debug("Key '%s' not in the previous state. Adding.", newRequest.first.c_str()); diff[newRequest.first] = newRequest.second; } else { if (previousRequests[newRequest.first].equals(newRequest.second)) { logger.debug("Key '%s' identical to the previous one. Skipping.", newRequest.first.c_str()); } else { logger.debug("Key '%s' differs from previous state's one. Adding.", newRequest.first.c_str()); diff[newRequest.first] = newRequest.second; } } } previousRequests.clear(); for (auto req : *(interface->getRequestMap())) { previousRequests.insert(req); } if (killSwitchActive) { for (auto &r : *(interface->getRequestMap())) { if (r.second.isKillSwitchDependent()) { logger.debug("Removing key '%s' because kill switch is active.", r.first.c_str()); diff.erase(r.first); previousRequests.erase(r.first); } } } return diff; }
//----------------------------------------------------------------------------- /// RemoveRequest /// /// Provides a way for the Server to remove a request that it will not be /// responding to. This is typical if the request is passed on to a plugin /// in a different process which will be sending a response. /// /// \param requestID a requestID returned by a call to CreateRequest //----------------------------------------------------------------------------- void RemoveRequest(CommunicationID requestID) { // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); RequestMap::iterator iter = g_requestMap.find(requestID); if (iter != g_requestMap.end()) { HTTPRequestHeader* pRequest = iter->second; delete pRequest; g_requestMap.erase(iter); } }
//----------------------------------------------------------------------------- /// GetRequestBinary /// /// This provides a method of accessing any binary data of a request. Requests /// that have binary data as the primary means of communication will have the /// string "binary" returned by GetRequestText(...); other commands may or may /// not have associated binary data, as determined by the client. /// /// \param requestID An ID for a particular request; the plugin will get this /// id as a parameter to the ProcessRequest( ) function /// /// \return pointer to the binary data that is associated with the request; /// NULL if no binary data exists //----------------------------------------------------------------------------- void* GetRequestBinary(CommunicationID requestID) { // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); HTTPRequestHeader* pRequest = NULL; RequestMap::iterator iterRequest = g_requestMap.find(requestID); if (iterRequest != g_requestMap.end()) { pRequest = iterRequest->second; } return pRequest; }
CommunicationID CreateRequest(HTTPRequestHeader* pRequest, bool bReceivedOverSocket) { // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); CommunicationID requestID = CommunicationID(pRequest->GetClientSocket()); pRequest->SetReceivedOverSocket(bReceivedOverSocket); if (g_requestMap.find(requestID) != g_requestMap.end()) { Log(logWARNING, "RequestID %u already exists from request: %s\n", requestID, pRequest->GetUrl()); // Remove the pre-existing request (required to cleanup memory). RemoveRequest(requestID); } g_requestMap[requestID] = pRequest; return requestID; }
//----------------------------------------------------------------------------- /// InitCommunication /// /// This function only needs to be called by wrapper plugins. It sets up the /// inter-process communication between the wrapper (which is in the app's /// process space) and the PerfServer. //----------------------------------------------------------------------------- bool InitCommunication(const char* strShortDescription, ProcessRequest_type pProcessRequestCallback) { unsigned long pid = osGetCurrentProcessId(); #ifdef _WIN32 sprintf_s(g_strSharedMemoryName, PS_MAX_PATH, "%lu/%s", pid, strShortDescription); #else // the '/' character can't be used as a filename in Linux, so just use the plugin name as the shared memory name // (ignore the process ID) sprintf_s(g_strSharedMemoryName, PS_MAX_PATH, "%lu %s", pid, strShortDescription); #endif if (smCreate(g_strSharedMemoryName, 100, sizeof(HTTPRequestHeader)) == false) { Log(logERROR, "InitCommunication: Can't open or create SharedMemory for %s.\n", strShortDescription); return false; } if (smOpen("PLUGINS_TO_GPS") == false) { smClose(g_strSharedMemoryName); Log(logERROR, "InitCommunication: Can't open SharedMemory for PLUGINS_TO_GPS.\n"); return false; } // store a local pointer to the ProcessRequest function g_processRequest = pProcessRequestCallback; if (g_processRequest == NULL) { smClose(g_strSharedMemoryName); Log(logERROR, "InitCommunication: ProcessRequest is NULL\n"); return false; } // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); g_requestMap.clear(); g_pBufferedResponse = NULL; g_uBufferedResponseSize = 0; return true; }
//----------------------------------------------------------------------------- bool MakeResponse(CommunicationID requestID, Response** ppResponse) { // protect the maps from being changed by other threads using the mutex ScopeLock lock(s_mutex); PsAssert(ppResponse != NULL); // first see if we already have this ID as a streaming response ResponseMap::iterator iterResponse = g_streamingResponseMap.find(requestID); if (iterResponse != g_streamingResponseMap.end()) { *ppResponse = iterResponse->second; return true; } // otherwise we need to create a new response based on the original request // so get the request RequestMap::iterator iterRequest = g_requestMap.find(requestID); if (iterRequest == g_requestMap.end()) { // the original request couldn't be found, so return failure return false; } // need to create a new response if (PsNew(*ppResponse) == false) { return false; } HTTPRequestHeader* pRequest = iterRequest->second; PsAssert(pRequest != NULL); if (pRequest->GetReceivedOverSocket() == true) { (*ppResponse)->client_socket = pRequest->GetClientSocket(); } else { #if defined (_WIN32) (*ppResponse)->client_socket = NetSocket::CreateFromDuplicate(pRequest->GetProtoInfo()); #else // create a new socket and connect to the streamSocket on the server (*ppResponse)->client_socket = NetSocket::Create(); if ((*ppResponse)->client_socket != NULL) { osPortAddress portAddress((unsigned short)pRequest->GetPort()); (*ppResponse)->client_socket->Connect(portAddress); } #endif } if ((*ppResponse)->client_socket == NULL) { int Err = NetSocket::LastError(); Log(logERROR, "Could not create socket: NetSocket failed with error: %ld\n", Err); return false; } // see if this should be added as a streaming response gtASCIIString strUrl(pRequest->GetUrl()); int32 iStream = strUrl.find(STR_STREAM_TOKEN); if (iStream >= 0) { const char* pBuf = strUrl.asCharArray(); const char* pRate = &pBuf[ iStream + strlen(STR_STREAM_TOKEN)]; unsigned int uRate = 0; // try to get the rate from the command; if (sscanf_s(pRate, "%u", &uRate) < 1) { // default to max rate uRate = COMM_MAX_STREAM_RATE; } // set the response as streaming with the specified rate (*ppResponse)->m_bStreamingEnabled = true; (*ppResponse)->m_dwMaxStreamsPerSecond = uRate; g_streamingResponseMap[ requestID ] = *ppResponse; } else { // streaming requests need to be kept around so that // additional responses can be directed to the right place, // HOWEVER, non-streaming requests only get a single response // and we just created the response for it, so it is safe // to remove the request from the requestMap. This will // help keep communication quick as the number of incoming // requests grows. RemoveRequest(requestID); } return true; }