123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- #include "stdafx.h"
- #include "baseEx.h"
- #include "CWebsocketServer.h"
- #include "mod_chromium.h"
- #include "base64.h"
- #include "CModTools.h"
- #include "cJSON.h"
- #include <Windows.h>
- #include <WinUser.h>
- #include "SpIni.h"
- #include "CSocketClient.h"
- #include "processControl.h"
- #include <boost/chrono.hpp>
- #include <boost/bind.hpp>
- #define COMPKEY_TERMINATE ((UINT_PTR) 0)
- #define COMPKEY_STATUS ((UINT_PTR) 1)
- #define COMPKEY_JOBOBJECT ((UINT_PTR) 2)
- namespace Chromium{
- CChromiumEntity::CChromiumEntity():m_pWsServer(NULL), m_iTcpBridgePort(4504),m_pTimerListener(NULL)
- {
- InitializeCriticalSection(&g_csInvokFreeRDP);
- DbgEx("CChromiumEntity constructor");
- /*
- m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
- boost::thread(boost::bind(&CChromiumEntity::JobNotify, this)).detach(); //后台自动运行
- m_job.Create(NULL, NULL);
- m_job.SetEndOfJobInfo(JOB_OBJECT_POST_AT_END_OF_JOB);
- m_job.AssociateCompletionPort(m_hIOCP, COMPKEY_JOBOBJECT);
- */
- boost::thread(boost::bind(&CChromiumEntity::CefClintNotify, this)).detach(); //后台自动运行
- }
- CChromiumEntity::~CChromiumEntity()
- {
- if (NULL != m_pWsServer)
- {
- free(m_pWsServer);
- m_pWsServer = NULL;
- }
- DeleteCriticalSection(&g_csInvokFreeRDP);
- }
- void CChromiumEntity::JobNotify() {
- TCHAR sz[2000];
- BOOL fDone = FALSE;
- while (!fDone) {
- DWORD dwBytesXferred;
- ULONG_PTR CompKey;
- LPOVERLAPPED po;
- GetQueuedCompletionStatus(m_hIOCP, &dwBytesXferred, &CompKey, &po, INFINITE);
- fDone = (CompKey == COMPKEY_TERMINATE);
- if (CompKey == COMPKEY_JOBOBJECT) {
- switch (dwBytesXferred) {
- case JOB_OBJECT_MSG_END_OF_JOB_TIME:
- Dbg("Job time limit reached");
- break;
- case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
- Dbg("Job process (Id=%d) time limit reached", po);
- break;
- case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
- Dbg("Too many active processes in job");
- break;
- case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
- Dbg("Job contains no active processes");
- break;
- case JOB_OBJECT_MSG_NEW_PROCESS:
- Dbg("New process (Id=%d) in Job", po);
- break;
- case JOB_OBJECT_MSG_EXIT_PROCESS:
- Dbg("Process (Id=%d) terminated", po);
- break;
- case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
- Dbg("Process (Id=%d) terminated abnormally", po);
- break;
- case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:
- Dbg("Process (Id=%d) exceeded memory limit", po);
- break;
- case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:
- Dbg("Process (Id=%d) exceeded job memory limit", po);
- break;
- default:
- Dbg("Unknown notification: %d", dwBytesXferred);
- break;
- }
- CompKey = 1;
- }
- }
- }
- void CChromiumEntity::OnPreStart(CAutoArray<CSimpleStringA> strArgs,CSmartPointer<ITransactionContext> pTransactionContext)
- {
- GetFunction()->GetSystemStaticInfo(m_sysInfo);
- //
- //MessageBox(0,0,0,0);
- // start tcp bridge service
- ErrorCodeEnum Error;
- CSmartPointer<IEntityFunction> spEntityFunction = GetFunction();
- Error = spEntityFunction->StartTcpBridgeServer(m_iTcpBridgePort);
-
- Error = spEntityFunction->SubscribeBroadcast("IEBrowser", "CustomerCmd", this, m_uidBrowserListenser);
- if (Error != Error_Succeed) {
- LOG_TRACE("subscribe browser CustomerCmd failed!");
- pTransactionContext->SendAnswer(Error);
- }
- else
- LOG_TRACE("subscribe browser CustomerCmd success!");
- if (Error != Error_Succeed)
- {
- LOG_TRACE("start tcp bridge server failed!");
- pTransactionContext->SendAnswer(Error);
- return;
- }
- // load all struct define xml & start websocket server
- CSimpleStringA strStructPath;
- GetFunction()->GetPath("Base", strStructPath);
- strStructPath.Append("\\res\\StructConfig\\");
- DbgEx("find struct config files in path %s", strStructPath);
- m_pWsServer = new CWebsocketServer(strStructPath, this);
- m_pWsServer->run();
-
- generateCefclientTimer();
- // 按照单屏方式
- pTransactionContext->SendAnswer(Error_Succeed);
- }
- void CChromiumEntity::OnPreClose(EntityCloseCauseEnum eCloseCause,CSmartPointer<ITransactionContext> pTransactionContext)
- {
- if (m_pTimerListener != NULL)
- {
- GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
- delete m_pTimerListener;
- m_pTimerListener = NULL;
- }
- GetFunction()->UnsubscribeBroadcast("IEBrowser");
- pTransactionContext->SendAnswer(Error_Succeed);
- }
- void CChromiumEntity::CefClintNotify()
- {
- while (1)
- {
- for (auto it = m_cefArr.begin(); it != m_cefArr.end(); it++)
- {
-
- if (WaitForSingleObject(std::get<1>(it->second), 10) != WAIT_OBJECT_0) //process end
- DbgEx("cefclient.exe tag:%s job:%d, process:%d checkOpen", it->first.c_str(), std::get<0>(it->second), std::get<1>(it->second));
- else
- {
- DbgEx("cefclient.exe tag:%s job:%d, process:%d check Closed, try to restart", it->first.c_str(), std::get<0>(it->second), std::get<1>(it->second));
- auto info = it->first;
- auto cmdline = std::get<2>(it->second);//save the param
- TerminateJobObject(std::get<0>(it->second), 0);
- m_cefArr.erase(it->first);
- auto startRet = startProcessInJob(cmdline, "");
- if (std::get<0>(startRet))
- m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), cmdline)));
- break;
- }
- }
- boost::this_thread::sleep_for(boost::chrono::seconds(10));
- }
- }
- void CChromiumEntity::OnCustomerCmd(const char* pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, IEBrowser::CustomerCmd& evt)
- {
- DbgEx("OnCustomerCmd %s", evt.cmdStr);
- static std::pair<int, std::string> historyChromium;
- auto jsDeletor = [](cJSON* p) {
- cJSON_Delete(p);
- };
- std::unique_ptr<cJSON, decltype(jsDeletor)> cmdJs(cJSON_Parse(evt.cmdStr), jsDeletor);
- auto cmdStr = cJSON_GetObjectItem(cmdJs.get(), "command")->valuestring;
- auto chromiumCLoseInJob = [&](std::string closeJob) -> bool{
- auto it = m_cefArr.find(closeJob);
- if (m_cefArr.end() != it)
- {
- TerminateJobObject(std::get<0>(it->second), 0);
- m_cefArr.erase(closeJob);
- return true;
- }
- else
- {
- DbgEx("m_cefArr can not find %s", closeJob.c_str());
- return false;
- }
- };
-
- if (!strcmp(cmdStr, "ChromiumStart"))
- {
- auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring;
- auto fullscreen = cJSON_GetObjectItem(cmdJs.get(), "fullscreen")->valueint;
- auto top = cJSON_GetObjectItem(cmdJs.get(), "top")->valueint;
- // 感觉应当通过FreeRDP来启动Chromium,不应该自己管理
- // 需要保存启动的chromium的句柄值或者进程Id,通过该进程Id来获取句柄管理chromium运行
- // 因为有多个chromium,此时对每一个H5页面,都保存连接。并无法感知到某一个chromium的连接都不存在来重启chromium
- /*
- if (historyChromium.second == info)
- {
- KillProcessById(historyChromium.first);
- historyChromium = std::make_pair(0, "");
- }
- */
- chromiumCLoseInJob(info);
- CSmartPointer<IConfigInfo> spCerConfig;
- GetFunction()->OpenConfig(Config_CenterSetting, spCerConfig);
- CSimpleString UserMgrUrlFulture;
- SpIniMappingTable table;
- // clean cache every time
- table.AddEntryString(GetEntityName(), "UserMgrUrlFulture", UserMgrUrlFulture, "");
- if (Error_Succeed != table.Load(spCerConfig))
- {
- DbgEx("load UserMgrUrlFulture failed");
- return;
- }
- CSimpleStringA strChromiumPath;
- GetFunction()->GetPath("Base", strChromiumPath);
- strChromiumPath.Append("\\bin\\Chromium\\");
- auto checkHttpThread = boost::async(boost::bind(checkHttpThreadFun, UserMgrUrlFulture.GetData()));
- checkHttpThread.wait_for(boost::chrono::seconds(1));
- Dbg("%s http check %s", UserMgrUrlFulture.GetData(), (checkHttpThread.is_ready() && checkHttpThread.get()) ? "sucess" : "failed");
- CSimpleStringA cachePath;
- GetFunction()->GetPath("Temp", cachePath);
- cachePath.Append("\\cefCache");
- CSimpleStringA strCmdLine = "";
- strCmdLine.Append(strChromiumPath).Append("cefclient.exe --url=").Append(UserMgrUrlFulture);
- strCmdLine.Append(" --hide-controls=true --cache-path=").Append(cachePath);
- strCmdLine.Append(" --logExtend=").Append(info).Append(" --top=").Append(std::to_string((LONGLONG)top).c_str());
- if (fullscreen)
- strCmdLine.Append(" --fullscreen");
- DbgEx("ChromiumStart cmdline : %s", strCmdLine);
- //control cefclient throught processId
- /*
- auto processInfo = StartProcess(strCmdLine.GetData(), "", true);
- if (nullptr != processInfo.first)
- historyChromium = std::make_pair(processInfo.second, info);
- else
- DbgEx("start chromium failed");
- */
- //control cefclient throught job
- auto startRet = startProcessInJob(strCmdLine.GetData(), "");
- if (std::get<0>(startRet))
- m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), strCmdLine.GetData())));
- else
- DbgEx("startProcessInJob failed!");
- }
- else if (!strcmp(cmdStr, "ChromiumClose"))
- {
- auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring;
- /*
- if (historyChromium.second == info)
- {
- KillProcessById(historyChromium.first);
- historyChromium = std::make_pair(0, "");
- }
- */
- chromiumCLoseInJob(info);
- }
-
- }
- void CChromiumEntity::generateCefclientTimer()
- {
- m_pTimerListener = new TimerOutHelper<CChromiumEntity>(this, &CChromiumEntity::OnTaskTimerListener, NULL, false);
- GetFunction()->SetTimer(CHROMIUM_TIMER_ID, m_pTimerListener, 10000); //间隔执行时间
- }
- void CChromiumEntity::OnTaskTimerListener(void *pData)
- {
- static int max_restartTime = 3;
- DbgEx("OnTaskTimerListener");
- if (max_restartTime == 0)
- {
- DbgEx("max_restartTime == 0, do not restart chromium");
- GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
- return;
- }
- static CModTools modTools(this);
- //基于Desk2S功能判断,当当前并未启动cefclient时,对cefclient.exe进行清理
- if (0 == m_cefArr.size())
- DbgEx("kill chromium %s", modTools.killAllChromium() ? "success" : "fail");
- auto rc = modTools.StartChromiumBrowser();
- DbgEx("TaskTimerListen, startChromiumBrowser, rc:%d, pid:%d", rc.first, rc.second);
-
- if (rc.first == Error_Succeed && rc.second != 0)
- {
- HANDLE defaultProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, rc.second);
- if (defaultProcess == nullptr)
- {
- DbgEx("OnTaskTimerListener, openProcess failed");
- return;
- }
- auto ret = assigntoJob(defaultProcess, rc.second);
- if (!ret.first)
- DbgEx("OnTaskTimerListener, assigntoJob failed");
- else
- {
- auto monitorThread = [&](HANDLE job, HANDLE process) {
- while (1)
- {
- if (WaitForSingleObject(process, 10) != WAIT_OBJECT_0) //process end
- DbgEx("default cefclient.exe job:%d, process:%d checkOpen", job, process);
- else
- {
- DbgEx("default cefclient.exe job:%d, process:%d check Closed, try to restart", job, process);
- TerminateJobObject(job, 0);
- max_restartTime--;
- generateCefclientTimer();
- //modTools.StartChromiumBrowser();
- break;
- }
- boost::this_thread::sleep_for(boost::chrono::seconds(10));
- }
- };
- if(ret.second != nullptr && defaultProcess != nullptr)
- boost::thread(monitorThread, ret.second, defaultProcess).detach();
- }
- }
-
- if (Error_Succeed != rc.first)
- {
- DbgEx("OnTaskTimerListener will be called after %d secs, rest restart time %d", 15, max_restartTime);
- if(max_restartTime)
- GetFunction()->ResetTimer(CHROMIUM_TIMER_ID, 15000);
- }else{
- DbgEx("OnTaskTimerListener rc = succeed");
- GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
- }
- }
- SP_BEGIN_ENTITY_MAP()
- SP_ENTITY(CChromiumEntity)
- SP_END_ENTITY_MAP()
- }
|