mod_chromium.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #include "stdafx.h"
  2. #include "baseEx.h"
  3. #include "CWebsocketServer.h"
  4. #include "mod_chromium.h"
  5. #include "base64.h"
  6. #include "CModTools.h"
  7. #include "cJSON.h"
  8. #include <Windows.h>
  9. #include <WinUser.h>
  10. #include "SpIni.h"
  11. #include "CSocketClient.h"
  12. #include "processControl.h"
  13. #include <boost/chrono.hpp>
  14. #include <boost/bind.hpp>
  15. #define COMPKEY_TERMINATE ((UINT_PTR) 0)
  16. #define COMPKEY_STATUS ((UINT_PTR) 1)
  17. #define COMPKEY_JOBOBJECT ((UINT_PTR) 2)
  18. namespace Chromium{
  19. CChromiumEntity::CChromiumEntity():m_pWsServer(NULL), m_iTcpBridgePort(4504),m_pTimerListener(NULL)
  20. {
  21. InitializeCriticalSection(&g_csInvokFreeRDP);
  22. DbgEx("CChromiumEntity constructor");
  23. /*
  24. m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
  25. boost::thread(boost::bind(&CChromiumEntity::JobNotify, this)).detach(); //后台自动运行
  26. m_job.Create(NULL, NULL);
  27. m_job.SetEndOfJobInfo(JOB_OBJECT_POST_AT_END_OF_JOB);
  28. m_job.AssociateCompletionPort(m_hIOCP, COMPKEY_JOBOBJECT);
  29. */
  30. boost::thread(boost::bind(&CChromiumEntity::CefClintNotify, this)).detach(); //后台自动运行
  31. }
  32. CChromiumEntity::~CChromiumEntity()
  33. {
  34. if (NULL != m_pWsServer)
  35. {
  36. free(m_pWsServer);
  37. m_pWsServer = NULL;
  38. }
  39. DeleteCriticalSection(&g_csInvokFreeRDP);
  40. }
  41. void CChromiumEntity::JobNotify() {
  42. TCHAR sz[2000];
  43. BOOL fDone = FALSE;
  44. while (!fDone) {
  45. DWORD dwBytesXferred;
  46. ULONG_PTR CompKey;
  47. LPOVERLAPPED po;
  48. GetQueuedCompletionStatus(m_hIOCP, &dwBytesXferred, &CompKey, &po, INFINITE);
  49. fDone = (CompKey == COMPKEY_TERMINATE);
  50. if (CompKey == COMPKEY_JOBOBJECT) {
  51. switch (dwBytesXferred) {
  52. case JOB_OBJECT_MSG_END_OF_JOB_TIME:
  53. Dbg("Job time limit reached");
  54. break;
  55. case JOB_OBJECT_MSG_END_OF_PROCESS_TIME:
  56. Dbg("Job process (Id=%d) time limit reached", po);
  57. break;
  58. case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:
  59. Dbg("Too many active processes in job");
  60. break;
  61. case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
  62. Dbg("Job contains no active processes");
  63. break;
  64. case JOB_OBJECT_MSG_NEW_PROCESS:
  65. Dbg("New process (Id=%d) in Job", po);
  66. break;
  67. case JOB_OBJECT_MSG_EXIT_PROCESS:
  68. Dbg("Process (Id=%d) terminated", po);
  69. break;
  70. case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
  71. Dbg("Process (Id=%d) terminated abnormally", po);
  72. break;
  73. case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT:
  74. Dbg("Process (Id=%d) exceeded memory limit", po);
  75. break;
  76. case JOB_OBJECT_MSG_JOB_MEMORY_LIMIT:
  77. Dbg("Process (Id=%d) exceeded job memory limit", po);
  78. break;
  79. default:
  80. Dbg("Unknown notification: %d", dwBytesXferred);
  81. break;
  82. }
  83. CompKey = 1;
  84. }
  85. }
  86. }
  87. void CChromiumEntity::OnPreStart(CAutoArray<CSimpleStringA> strArgs,CSmartPointer<ITransactionContext> pTransactionContext)
  88. {
  89. GetFunction()->GetSystemStaticInfo(m_sysInfo);
  90. //
  91. //MessageBox(0,0,0,0);
  92. // start tcp bridge service
  93. ErrorCodeEnum Error;
  94. CSmartPointer<IEntityFunction> spEntityFunction = GetFunction();
  95. Error = spEntityFunction->StartTcpBridgeServer(m_iTcpBridgePort);
  96. Error = spEntityFunction->SubscribeBroadcast("IEBrowser", "CustomerCmd", this, m_uidBrowserListenser);
  97. if (Error != Error_Succeed) {
  98. LOG_TRACE("subscribe browser CustomerCmd failed!");
  99. pTransactionContext->SendAnswer(Error);
  100. }
  101. else
  102. LOG_TRACE("subscribe browser CustomerCmd success!");
  103. if (Error != Error_Succeed)
  104. {
  105. LOG_TRACE("start tcp bridge server failed!");
  106. pTransactionContext->SendAnswer(Error);
  107. return;
  108. }
  109. // load all struct define xml & start websocket server
  110. CSimpleStringA strStructPath;
  111. GetFunction()->GetPath("Base", strStructPath);
  112. strStructPath.Append("\\res\\StructConfig\\");
  113. DbgEx("find struct config files in path %s", strStructPath);
  114. m_pWsServer = new CWebsocketServer(strStructPath, this);
  115. m_pWsServer->run();
  116. generateCefclientTimer();
  117. // 按照单屏方式
  118. pTransactionContext->SendAnswer(Error_Succeed);
  119. }
  120. void CChromiumEntity::OnPreClose(EntityCloseCauseEnum eCloseCause,CSmartPointer<ITransactionContext> pTransactionContext)
  121. {
  122. if (m_pTimerListener != NULL)
  123. {
  124. GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
  125. delete m_pTimerListener;
  126. m_pTimerListener = NULL;
  127. }
  128. GetFunction()->UnsubscribeBroadcast("IEBrowser");
  129. pTransactionContext->SendAnswer(Error_Succeed);
  130. }
  131. void CChromiumEntity::CefClintNotify()
  132. {
  133. while (1)
  134. {
  135. for (auto it = m_cefArr.begin(); it != m_cefArr.end(); it++)
  136. {
  137. if (WaitForSingleObject(std::get<1>(it->second), 10) != WAIT_OBJECT_0) //process end
  138. DbgEx("cefclient.exe tag:%s job:%d, process:%d checkOpen", it->first.c_str(), std::get<0>(it->second), std::get<1>(it->second));
  139. else
  140. {
  141. 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));
  142. auto info = it->first;
  143. auto cmdline = std::get<2>(it->second);//save the param
  144. TerminateJobObject(std::get<0>(it->second), 0);
  145. m_cefArr.erase(it->first);
  146. auto startRet = startProcessInJob(cmdline, "");
  147. if (std::get<0>(startRet))
  148. m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), cmdline)));
  149. break;
  150. }
  151. }
  152. boost::this_thread::sleep_for(boost::chrono::seconds(10));
  153. }
  154. }
  155. void CChromiumEntity::OnCustomerCmd(const char* pszEntityName, DWORD dwMessageId, DWORD dwMessageSignature, IEBrowser::CustomerCmd& evt)
  156. {
  157. DbgEx("OnCustomerCmd %s", evt.cmdStr);
  158. static std::pair<int, std::string> historyChromium;
  159. auto jsDeletor = [](cJSON* p) {
  160. cJSON_Delete(p);
  161. };
  162. std::unique_ptr<cJSON, decltype(jsDeletor)> cmdJs(cJSON_Parse(evt.cmdStr), jsDeletor);
  163. auto cmdStr = cJSON_GetObjectItem(cmdJs.get(), "command")->valuestring;
  164. auto chromiumCLoseInJob = [&](std::string closeJob) -> bool{
  165. auto it = m_cefArr.find(closeJob);
  166. if (m_cefArr.end() != it)
  167. {
  168. TerminateJobObject(std::get<0>(it->second), 0);
  169. m_cefArr.erase(closeJob);
  170. return true;
  171. }
  172. else
  173. {
  174. DbgEx("m_cefArr can not find %s", closeJob.c_str());
  175. return false;
  176. }
  177. };
  178. if (!strcmp(cmdStr, "ChromiumStart"))
  179. {
  180. auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring;
  181. auto fullscreen = cJSON_GetObjectItem(cmdJs.get(), "fullscreen")->valueint;
  182. auto top = cJSON_GetObjectItem(cmdJs.get(), "top")->valueint;
  183. // 感觉应当通过FreeRDP来启动Chromium,不应该自己管理
  184. // 需要保存启动的chromium的句柄值或者进程Id,通过该进程Id来获取句柄管理chromium运行
  185. // 因为有多个chromium,此时对每一个H5页面,都保存连接。并无法感知到某一个chromium的连接都不存在来重启chromium
  186. /*
  187. if (historyChromium.second == info)
  188. {
  189. KillProcessById(historyChromium.first);
  190. historyChromium = std::make_pair(0, "");
  191. }
  192. */
  193. chromiumCLoseInJob(info);
  194. CSmartPointer<IConfigInfo> spCerConfig;
  195. GetFunction()->OpenConfig(Config_CenterSetting, spCerConfig);
  196. CSimpleString UserMgrUrlFulture;
  197. SpIniMappingTable table;
  198. // clean cache every time
  199. table.AddEntryString(GetEntityName(), "UserMgrUrlFulture", UserMgrUrlFulture, "");
  200. if (Error_Succeed != table.Load(spCerConfig))
  201. {
  202. DbgEx("load UserMgrUrlFulture failed");
  203. return;
  204. }
  205. CSimpleStringA strChromiumPath;
  206. GetFunction()->GetPath("Base", strChromiumPath);
  207. strChromiumPath.Append("\\bin\\Chromium\\");
  208. auto checkHttpThread = boost::async(boost::bind(checkHttpThreadFun, UserMgrUrlFulture.GetData()));
  209. checkHttpThread.wait_for(boost::chrono::seconds(1));
  210. Dbg("%s http check %s", UserMgrUrlFulture.GetData(), (checkHttpThread.is_ready() && checkHttpThread.get()) ? "sucess" : "failed");
  211. CSimpleStringA cachePath;
  212. GetFunction()->GetPath("Temp", cachePath);
  213. cachePath.Append("\\cefCache");
  214. CSimpleStringA strCmdLine = "";
  215. strCmdLine.Append(strChromiumPath).Append("cefclient.exe --url=").Append(UserMgrUrlFulture);
  216. strCmdLine.Append(" --hide-controls=true --cache-path=").Append(cachePath);
  217. strCmdLine.Append(" --logExtend=").Append(info).Append(" --top=").Append(std::to_string((LONGLONG)top).c_str());
  218. if (fullscreen)
  219. strCmdLine.Append(" --fullscreen");
  220. DbgEx("ChromiumStart cmdline : %s", strCmdLine);
  221. //control cefclient throught processId
  222. /*
  223. auto processInfo = StartProcess(strCmdLine.GetData(), "", true);
  224. if (nullptr != processInfo.first)
  225. historyChromium = std::make_pair(processInfo.second, info);
  226. else
  227. DbgEx("start chromium failed");
  228. */
  229. //control cefclient throught job
  230. auto startRet = startProcessInJob(strCmdLine.GetData(), "");
  231. if (std::get<0>(startRet))
  232. m_cefArr.insert(std::make_pair(info, std::make_tuple(std::get<1>(startRet), std::get<2>(startRet), strCmdLine.GetData())));
  233. else
  234. DbgEx("startProcessInJob failed!");
  235. }
  236. else if (!strcmp(cmdStr, "ChromiumClose"))
  237. {
  238. auto info = cJSON_GetObjectItem(cmdJs.get(), "info")->valuestring;
  239. /*
  240. if (historyChromium.second == info)
  241. {
  242. KillProcessById(historyChromium.first);
  243. historyChromium = std::make_pair(0, "");
  244. }
  245. */
  246. chromiumCLoseInJob(info);
  247. }
  248. }
  249. void CChromiumEntity::generateCefclientTimer()
  250. {
  251. m_pTimerListener = new TimerOutHelper<CChromiumEntity>(this, &CChromiumEntity::OnTaskTimerListener, NULL, false);
  252. GetFunction()->SetTimer(CHROMIUM_TIMER_ID, m_pTimerListener, 10000); //间隔执行时间
  253. }
  254. void CChromiumEntity::OnTaskTimerListener(void *pData)
  255. {
  256. static int max_restartTime = 3;
  257. DbgEx("OnTaskTimerListener");
  258. if (max_restartTime == 0)
  259. {
  260. DbgEx("max_restartTime == 0, do not restart chromium");
  261. GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
  262. return;
  263. }
  264. static CModTools modTools(this);
  265. //基于Desk2S功能判断,当当前并未启动cefclient时,对cefclient.exe进行清理
  266. if (0 == m_cefArr.size())
  267. DbgEx("kill chromium %s", modTools.killAllChromium() ? "success" : "fail");
  268. auto rc = modTools.StartChromiumBrowser();
  269. DbgEx("TaskTimerListen, startChromiumBrowser, rc:%d, pid:%d", rc.first, rc.second);
  270. if (rc.first == Error_Succeed && rc.second != 0)
  271. {
  272. HANDLE defaultProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, rc.second);
  273. if (defaultProcess == nullptr)
  274. {
  275. DbgEx("OnTaskTimerListener, openProcess failed");
  276. return;
  277. }
  278. auto ret = assigntoJob(defaultProcess, rc.second);
  279. if (!ret.first)
  280. DbgEx("OnTaskTimerListener, assigntoJob failed");
  281. else
  282. {
  283. auto monitorThread = [&](HANDLE job, HANDLE process) {
  284. while (1)
  285. {
  286. if (WaitForSingleObject(process, 10) != WAIT_OBJECT_0) //process end
  287. DbgEx("default cefclient.exe job:%d, process:%d checkOpen", job, process);
  288. else
  289. {
  290. DbgEx("default cefclient.exe job:%d, process:%d check Closed, try to restart", job, process);
  291. TerminateJobObject(job, 0);
  292. max_restartTime--;
  293. generateCefclientTimer();
  294. //modTools.StartChromiumBrowser();
  295. break;
  296. }
  297. boost::this_thread::sleep_for(boost::chrono::seconds(10));
  298. }
  299. };
  300. if(ret.second != nullptr && defaultProcess != nullptr)
  301. boost::thread(monitorThread, ret.second, defaultProcess).detach();
  302. }
  303. }
  304. if (Error_Succeed != rc.first)
  305. {
  306. DbgEx("OnTaskTimerListener will be called after %d secs, rest restart time %d", 15, max_restartTime);
  307. if(max_restartTime)
  308. GetFunction()->ResetTimer(CHROMIUM_TIMER_ID, 15000);
  309. }else{
  310. DbgEx("OnTaskTimerListener rc = succeed");
  311. GetFunction()->KillTimer(CHROMIUM_TIMER_ID);
  312. }
  313. }
  314. SP_BEGIN_ENTITY_MAP()
  315. SP_ENTITY(CChromiumEntity)
  316. SP_END_ENTITY_MAP()
  317. }