54 std::shared_ptr<ROOT::Experimental::RWebWindowsManager> &ROOT::Experimental::RWebWindowsManager::Instance()
56 static std::shared_ptr<RWebWindowsManager> sInstance = std::make_shared<ROOT::Experimental::RWebWindowsManager>();
68 static std::thread::id gWebWinMainThrd = std::this_thread::get_id();
74 bool ROOT::Experimental::RWebWindowsManager::IsMainThrd()
76 return std::this_thread::get_id() == gWebWinMainThrd;
83 ROOT::Experimental::RWebWindowsManager::RWebWindowsManager() =
default;
89 ROOT::Experimental::RWebWindowsManager::~RWebWindowsManager()
91 if (gApplication && fServer && !fServer->IsTerminated()) {
92 gApplication->Disconnect(
"Terminate(Int_t)", fServer.get(),
"SetTerminate()");
93 fServer->SetTerminate();
141 bool ROOT::Experimental::RWebWindowsManager::CreateServer(
bool with_http)
144 std::lock_guard<std::recursive_mutex> grd(fMutex);
148 fServer = std::make_unique<THttpServer>(
"basic_sniffer");
150 const char *serv_thrd = gEnv->GetValue(
"WebGui.HttpThrd",
"");
151 if (serv_thrd && strstr(serv_thrd,
"yes"))
153 else if (serv_thrd && strstr(serv_thrd,
"no"))
154 fUseHttpThrd =
false;
156 const char *send_thrds = gEnv->GetValue(
"WebGui.SenderThrds",
"");
157 if (send_thrds && *send_thrds) {
158 if (strstr(send_thrds,
"yes"))
159 fUseSenderThreads =
true;
160 else if (strstr(send_thrds,
"no"))
161 fUseSenderThreads =
false;
163 R__ERROR_HERE(
"WebDisplay") <<
"WebGui.SenderThrds has to be yes or no";
166 if (IsUseHttpThread())
167 fServer->CreateServerThread();
170 gApplication->Connect(
"Terminate(Int_t)",
"THttpServer", fServer.get(),
"SetTerminate()");
175 TString ui5dir = gSystem->Getenv(
"ROOTUI5SYS");
176 if (ui5dir.Length() == 0)
177 ui5dir = gEnv->GetValue(
"WebGui.RootUi5Path",
"");
179 if (ui5dir.Length() == 0)
180 ui5dir.Form(
"%s/ui5", TROOT::GetDataDir().Data());
182 if (gSystem->ExpandPathName(ui5dir)) {
183 R__ERROR_HERE(
"WebDisplay") <<
"Path to ROOT ui5 sources " << ui5dir <<
" not found, set ROOTUI5SYS correctly";
187 fServer->AddLocation(
"rootui5sys/", ui5dir.Data());
190 if (!with_http || !fAddr.empty())
193 int http_port = gEnv->GetValue(
"WebGui.HttpPort", 0);
194 int http_min = gEnv->GetValue(
"WebGui.HttpPortMin", 8800);
195 int http_max = gEnv->GetValue(
"WebGui.HttpPortMax", 9800);
196 int http_wstmout = gEnv->GetValue(
"WebGui.HttpWSTmout", 10000);
197 int http_maxage = gEnv->GetValue(
"WebGui.HttpMaxAge", -1);
198 fLaunchTmout = gEnv->GetValue(
"WebGui.LaunchTmout", 30.);
199 const char *http_loopback = gEnv->GetValue(
"WebGui.HttpLoopback",
"no");
200 const char *http_bind = gEnv->GetValue(
"WebGui.HttpBind",
"");
201 const char *http_ssl = gEnv->GetValue(
"WebGui.UseHttps",
"no");
202 const char *ssl_cert = gEnv->GetValue(
"WebGui.ServerCert",
"rootserver.pem");
204 bool assign_loopback = http_loopback && strstr(http_loopback,
"yes");
205 bool use_secure = http_ssl && strstr(http_ssl,
"yes");
209 R__ERROR_HERE(
"WebDisplay") <<
"Not allowed to create real HTTP server, check WebGui.HttpPort variable";
216 if (http_max - http_min < ntry)
217 ntry = http_max - http_min;
219 while (ntry-- >= 0) {
221 if ((http_min <= 0) || (http_max <= http_min)) {
222 R__ERROR_HERE(
"WebDisplay") <<
"Wrong HTTP range configuration, check WebGui.HttpPortMin/Max variables";
226 http_port = (int)(http_min + (http_max - http_min) * gRandom->Rndm(1));
229 TString engine, url(use_secure ?
"https://" :
"http://");
230 engine.Form(
"%s:%d?websocket_timeout=%d", (use_secure ?
"https" :
"http"), http_port, http_wstmout);
231 if (assign_loopback) {
232 engine.Append(
"&loopback");
233 url.Append(
"localhost");
234 }
else if (http_bind && (strlen(http_bind) > 0)) {
235 engine.Append(
"&bind=");
236 engine.Append(http_bind);
237 url.Append(http_bind);
239 url.Append(
"localhost");
242 if (http_maxage >= 0)
243 engine.Append(TString::Format(
"&max_age=%d", http_maxage));
246 engine.Append(
"&ssl_cert=");
247 engine.Append(ssl_cert);
250 if (fServer->CreateEngine(engine)) {
253 fAddr.append(std::to_string(http_port));
267 std::shared_ptr<ROOT::Experimental::RWebWindow> ROOT::Experimental::RWebWindowsManager::CreateWindow()
271 std::lock_guard<std::recursive_mutex> grd(fMutex);
273 if (!CreateServer()) {
274 R__ERROR_HERE(
"WebDisplay") <<
"Cannot create server when creating window";
278 std::shared_ptr<ROOT::Experimental::RWebWindow> win = std::make_shared<ROOT::Experimental::RWebWindow>();
281 R__ERROR_HERE(
"WebDisplay") <<
"Fail to create RWebWindow instance";
285 double dflt_tmout = gEnv->GetValue(
"WebGui.OperationTmout", 50.);
287 auto wshandler = win->CreateWSHandler(Instance(), ++fIdCnt, dflt_tmout);
289 if (gEnv->GetValue(
"WebGui.RecordData", 0) > 0) {
290 std::string fname, prefix;
292 prefix = std::string(
"f") + std::to_string(fIdCnt) +
"_";
293 fname = std::string(
"protcol") + std::to_string(fIdCnt) +
".json";
295 fname =
"protocol.json";
297 win->RecordData(fname, prefix);
300 fServer->RegisterWS(wshandler);
309 void ROOT::Experimental::RWebWindowsManager::Unregister(ROOT::Experimental::RWebWindow &win)
312 fServer->UnregisterWS(win.fWSHandler);
318 std::string ROOT::Experimental::RWebWindowsManager::GetUrl(
const ROOT::Experimental::RWebWindow &win,
bool remote)
321 R__ERROR_HERE(
"WebDisplay") <<
"Server instance not exists when requesting window URL";
325 std::string addr =
"/";
327 addr.append(win.fWSHandler->GetName());
332 if (!CreateServer(
true)) {
333 R__ERROR_HERE(
"WebDisplay") <<
"Fail to start real HTTP server when requesting URL";
378 unsigned ROOT::Experimental::RWebWindowsManager::ShowWindow(RWebWindow &win,
bool batch_mode,
const RWebDisplayArgs &user_args)
381 if (!batch_mode && gROOT->IsWebDisplayBatch())
385 if (user_args.GetBrowserKind() == RWebDisplayArgs::kEmbedded)
389 std::lock_guard<std::recursive_mutex> grd(fMutex);
392 R__ERROR_HERE(
"WebDisplay") <<
"Server instance not exists to show window";
400 key = std::to_string(gRandom->Integer(0x100000));
401 }
while ((--ntry > 0) && win.HasKey(key));
403 R__ERROR_HERE(
"WebDisplay") <<
"Fail to create unique key for the window";
407 RWebDisplayArgs args(user_args);
409 if (batch_mode && !args.IsSupportHeadless()) {
410 R__ERROR_HERE(
"WebDisplay") <<
"Cannot use batch mode with " << args.GetBrowserName();
414 args.SetHeadless(batch_mode);
415 if (args.GetWidth() <= 0) args.SetWidth(win.GetWidth());
416 if (args.GetHeight() <= 0) args.SetHeight(win.GetHeight());
418 bool normal_http = !args.IsLocalDisplay();
419 if (!normal_http && (gEnv->GetValue(
"WebGui.ForceHttp",0) == 1))
422 std::string url = GetUrl(win, normal_http);
424 R__ERROR_HERE(
"WebDisplay") <<
"Cannot create URL for the window";
430 args.AppendUrlOpt(std::string(
"key=") + key);
431 if (batch_mode) args.AppendUrlOpt(
"batch_mode");
434 args.SetHttpServer(GetServer());
436 auto handle = RWebDisplayHandle::Display(args);
439 R__ERROR_HERE(
"WebDisplay") <<
"Cannot display window in " << args.GetBrowserName();
443 return win.AddDisplayHandle(batch_mode, key, handle);
456 int ROOT::Experimental::RWebWindowsManager::WaitFor(RWebWindow &win, WebWindowWaitFunc_t check,
bool timed,
double timelimit)
462 auto start = std::chrono::high_resolution_clock::now();
466 while ((res = check(spent)) == 0) {
469 gSystem->ProcessEvents();
473 std::this_thread::sleep_for(std::chrono::milliseconds(1));
475 std::chrono::duration<double, std::milli> elapsed = std::chrono::high_resolution_clock::now() - start;
477 spent = elapsed.count() * 1e-3;
479 if (timed && (spent > timelimit))
491 void ROOT::Experimental::RWebWindowsManager::Terminate()
494 fServer->SetTerminate();
497 gApplication->Terminate();