Logo ROOT  
Reference Guide
RWebWindow.cxx
Go to the documentation of this file.
1/// \file RWebWindow.cxx
2/// \ingroup WebGui ROOT7
3/// \author Sergey Linev <s.linev@gsi.de>
4/// \date 2017-10-16
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#include <ROOT/RWebWindow.hxx>
17
19#include <ROOT/RLogger.hxx>
20
22#include "THttpCallArg.h"
23#include "TUrl.h"
24#include "TROOT.h"
25
26#include <cstring>
27#include <cstdlib>
28#include <utility>
29#include <assert.h>
30#include <algorithm>
31#include <fstream>
32
33using namespace std::string_literals;
34
35//////////////////////////////////////////////////////////////////////////////////////////
36/// Destructor for WebConn
37/// Notify special HTTP request which blocks headless browser from exit
38
40{
41 if (fHold) {
42 fHold->SetTextContent("console.log('execute holder script'); if (window) setTimeout (window.close, 1000); if (window) window.close();");
43 fHold->NotifyCondition();
44 fHold.reset();
45 }
46}
47
48
49/** \class ROOT::Experimental::RWebWindow
50\ingroup webdisplay
51
52Represents web window, which can be shown in web browser or any other supported environment
53
54Window can be configured to run either in the normal or in the batch (headless) mode.
55In second case no any graphical elements will be created. For the normal window one can configure geometry
56(width and height), which are applied when window shown.
57
58Each window can be shown several times (if allowed) in different places - either as the
59CEF (chromium embedded) window or in the standard web browser. When started, window will open and show
60HTML page, configured with RWebWindow::SetDefaultPage() method.
61
62Typically (but not necessarily) clients open web socket connection to the window and one can exchange data,
63using RWebWindow::Send() method and call-back function assigned via RWebWindow::SetDataCallBack().
64
65*/
66
67
68//////////////////////////////////////////////////////////////////////////////////////////
69/// RWebWindow constructor
70/// Should be defined here because of std::unique_ptr<RWebWindowWSHandler>
71
73
74//////////////////////////////////////////////////////////////////////////////////////////
75/// RWebWindow destructor
76/// Closes all connections and remove window from manager
77
79{
80 if (fMaster)
81 fMaster->RemoveEmbedWindow(fMasterConnId, fMasterChannel);
82
83 if (fWSHandler)
84 fWSHandler->SetDisabled();
85
86 if (fMgr) {
87
88 // make copy of all connections
89 auto lst = GetConnections();
90
91 {
92 // clear connections vector under mutex
93 std::lock_guard<std::mutex> grd(fConnMutex);
94 fConn.clear();
95 fPendingConn.clear();
96 }
97
98 for (auto &conn : lst) {
99 conn->fActive = false;
100 for (auto &elem: conn->fEmbed)
101 elem.second->fMaster.reset();
102 }
103
104 fMgr->Unregister(*this);
105 }
106}
107
108//////////////////////////////////////////////////////////////////////////////////////////
109/// Configure window to show some of existing JSROOT panels
110/// It uses "file:rootui5sys/panel/panel.html" as default HTML page
111/// At the moment only FitPanel is existing
112
114{
115 {
116 std::lock_guard<std::mutex> grd(fConnMutex);
117 if (!fConn.empty()) {
118 R__ERROR_HERE("webgui") << "Cannot configure panel when connection exists";
119 return;
120 }
121 }
122
124 SetDefaultPage("file:rootui5sys/panel/panel.html");
125}
126
127//////////////////////////////////////////////////////////////////////////////////////////
128/// Assigns manager reference, window id and creates websocket handler, used for communication with the clients
129
130std::shared_ptr<ROOT::Experimental::RWebWindowWSHandler>
131ROOT::Experimental::RWebWindow::CreateWSHandler(std::shared_ptr<RWebWindowsManager> mgr, unsigned id, double tmout)
132{
133 fMgr = mgr;
134 fId = id;
135 fOperationTmout = tmout;
136
137 fSendMT = fMgr->IsUseSenderThreads();
138 fWSHandler = std::make_shared<RWebWindowWSHandler>(*this, Form("win%u", GetId()));
139
140 return fWSHandler;
141}
142
143//////////////////////////////////////////////////////////////////////////////////////////
144/// Return URL string to access web window
145/// If remote flag is specified, real HTTP server will be started automatically
146
148{
149 return fMgr->GetUrl(*this, remote);
150}
151
152//////////////////////////////////////////////////////////////////////////////////////////
153/// Return THttpServer instance serving requests to the window
154
156{
157 return fMgr->GetServer();
158}
159
160//////////////////////////////////////////////////////////////////////////////////////////
161/// Show window in specified location
162/// See ROOT::Experimental::RWebWindowsManager::Show() docu for more info
163/// returns (future) connection id (or 0 when fails)
164
166{
167 return fMgr->ShowWindow(*this, false, args);
168}
169
170//////////////////////////////////////////////////////////////////////////////////////////
171/// Create batch job for specified window
172/// Normally only single batch job is used, but many can be created
173/// See ROOT::Experimental::RWebWindowsManager::Show() docu for more info
174/// returns (future) connection id (or 0 when fails)
175
176unsigned ROOT::Experimental::RWebWindow::MakeBatch(bool create_new, const RWebDisplayArgs &args)
177{
178 unsigned connid = 0;
179 if (!create_new)
180 connid = FindBatch();
181 if (!connid)
182 connid = fMgr->ShowWindow(*this, true, args);
183 return connid;
184}
185
186//////////////////////////////////////////////////////////////////////////////////////////
187/// Returns connection id of batch job
188/// Connection to that job may not be initialized yet
189/// If connection does not exists, returns 0
190
192{
193 std::lock_guard<std::mutex> grd(fConnMutex);
194
195 for (auto &entry : fPendingConn) {
196 if (entry->fBatchMode)
197 return entry->fConnId;
198 }
199
200 for (auto &conn : fConn) {
201 if (conn->fBatchMode)
202 return conn->fConnId;
203 }
204
205 return 0;
206}
207
208//////////////////////////////////////////////////////////////////////////////////////////
209/// Returns first connection id where window is displayed
210/// It could be that connection(s) not yet fully established - but also not timed out
211/// Batch jobs will be ignored here
212/// Returns 0 if connection not exists
213
215{
216 std::lock_guard<std::mutex> grd(fConnMutex);
217
218 for (auto &entry : fPendingConn) {
219 if (!entry->fBatchMode)
220 return entry->fConnId;
221 }
222
223 for (auto &conn : fConn) {
224 if (!conn->fBatchMode)
225 return conn->fConnId;
226 }
227
228 return 0;
229}
230
231//////////////////////////////////////////////////////////////////////////////////////////
232/// Find connection with given websocket id
233/// Connection mutex should be locked before method calling
234
235std::shared_ptr<ROOT::Experimental::RWebWindow::WebConn> ROOT::Experimental::RWebWindow::FindOrCreateConnection(unsigned wsid, bool make_new, const char *query)
236{
237 std::lock_guard<std::mutex> grd(fConnMutex);
238
239 for (auto &conn : fConn) {
240 if (conn->fWSId == wsid)
241 return conn;
242 }
243
244 // put code to create new connection here to stay under same locked mutex
245 if (make_new) {
246 // check if key was registered already
247
248 std::shared_ptr<WebConn> key;
249 std::string keyvalue;
250
251 if (query) {
252 TUrl url;
253 url.SetOptions(query);
254 if (url.HasOption("key"))
255 keyvalue = url.GetValueFromOptions("key");
256 }
257
258 if (!keyvalue.empty())
259 for (size_t n = 0; n < fPendingConn.size(); ++n)
260 if (fPendingConn[n]->fKey == keyvalue) {
261 key = std::move(fPendingConn[n]);
262 fPendingConn.erase(fPendingConn.begin() + n);
263 break;
264 }
265
266 if (key) {
267 key->fWSId = wsid;
268 key->fActive = true;
269 key->ResetStamps(); // TODO: probably, can be moved outside locked area
270 fConn.emplace_back(key);
271 } else {
272 fConn.emplace_back(std::make_shared<WebConn>(++fConnCnt, wsid));
273 }
274 }
275
276 return nullptr;
277}
278
279//////////////////////////////////////////////////////////////////////////////////////////
280/// Remove connection with given websocket id
281
282std::shared_ptr<ROOT::Experimental::RWebWindow::WebConn> ROOT::Experimental::RWebWindow::RemoveConnection(unsigned wsid)
283{
284
285 std::shared_ptr<WebConn> res;
286
287 {
288 std::lock_guard<std::mutex> grd(fConnMutex);
289
290 for (size_t n = 0; n < fConn.size(); ++n)
291 if (fConn[n]->fWSId == wsid) {
292 res = std::move(fConn[n]);
293 fConn.erase(fConn.begin() + n);
294 res->fActive = false;
295 break;
296 }
297 }
298
299 if (res)
300 for (auto &elem: res->fEmbed)
301 elem.second->fMaster.reset();
302
303 return res;
304}
305
306//////////////////////////////////////////////////////////////////////////////////////////
307/// Process special http request, used to hold headless browser running
308/// Such requests should not be replied for the long time
309/// Be aware that function called directly from THttpServer thread, which is not same thread as window
310
311bool ROOT::Experimental::RWebWindow::ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg)
312{
313 std::string query = arg->GetQuery();
314
315 if (query.compare(0, 4, "key=") != 0)
316 return false;
317
318 std::string key = query.substr(4);
319
320 std::shared_ptr<THttpCallArg> prev;
321
322 bool found_key = false;
323
324 // use connection mutex to access hold request
325 {
326 std::lock_guard<std::mutex> grd(fConnMutex);
327 for (auto &entry : fPendingConn) {
328 if (entry->fKey == key) {
329 assert(!found_key); // indicate error if many same keys appears
330 found_key = true;
331 prev = std::move(entry->fHold);
332 entry->fHold = arg;
333 }
334 }
335
336
337 for (auto &conn : fConn) {
338 if (conn->fKey == key) {
339 assert(!found_key); // indicate error if many same keys appears
340 prev = std::move(conn->fHold);
341 conn->fHold = arg;
342 found_key = true;
343 }
344 }
345 }
346
347 if (prev) {
348 prev->SetTextContent("console.log('execute holder script'); if (window) window.close();");
349 prev->NotifyCondition();
350 }
351
352 return found_key;
353}
354
355//////////////////////////////////////////////////////////////////////////////////////////
356/// Provide data to user callback
357/// User callback must be executed in the window thread
358
359void ROOT::Experimental::RWebWindow::ProvideQueueEntry(unsigned connid, EQueueEntryKind kind, std::string &&arg)
360{
361 {
362 std::lock_guard<std::mutex> grd(fInputQueueMutex);
363 fInputQueue.emplace(connid, kind, std::move(arg));
364 }
365
367}
368
369//////////////////////////////////////////////////////////////////////////////////////////
370/// Invoke callbacks with existing data
371/// Must be called from appropriate thread
372
374{
375 if (fCallbacksThrdIdSet && (fCallbacksThrdId != std::this_thread::get_id()) && !force)
376 return;
377
378 while (true) {
379 unsigned connid;
380 EQueueEntryKind kind;
381 std::string arg;
382
383 {
384 std::lock_guard<std::mutex> grd(fInputQueueMutex);
385 if (fInputQueue.size() == 0)
386 return;
387 auto &entry = fInputQueue.front();
388 connid = entry.fConnId;
389 kind = entry.fKind;
390 arg = std::move(entry.fData);
391 fInputQueue.pop();
392 }
393
394 switch (kind) {
395 case kind_None: break;
396 case kind_Connect:
397 if (fConnCallback)
398 fConnCallback(connid);
399 break;
400 case kind_Data:
401 if (fDataCallback)
402 fDataCallback(connid, arg);
403 break;
404 case kind_Disconnect:
406 fDisconnCallback(connid);
407 break;
408 }
409 }
410}
411
412//////////////////////////////////////////////////////////////////////////////////////////
413/// Add display handle and associated key
414/// Key is random number generated when starting new window
415/// When client is connected, key should be supplied to correctly identify it
416
417unsigned ROOT::Experimental::RWebWindow::AddDisplayHandle(bool batch_mode, const std::string &key, std::unique_ptr<RWebDisplayHandle> &handle)
418{
419 std::lock_guard<std::mutex> grd(fConnMutex);
420
421 ++fConnCnt;
422
423 auto conn = std::make_shared<WebConn>(fConnCnt, batch_mode, key);
424
425 std::swap(conn->fDisplayHandle, handle);
426
427 fPendingConn.emplace_back(conn);
428
429 return fConnCnt;
430}
431
432//////////////////////////////////////////////////////////////////////////////////////////
433/// Returns true if provided key value already exists (in processes map or in existing connections)
434
435bool ROOT::Experimental::RWebWindow::HasKey(const std::string &key) const
436{
437 std::lock_guard<std::mutex> grd(fConnMutex);
438
439 for (auto &entry : fPendingConn) {
440 if (entry->fKey == key)
441 return true;
442 }
443
444 for (auto &conn : fConn) {
445 if (conn->fKey == key)
446 return true;
447 }
448
449 return false;
450}
451
452//////////////////////////////////////////////////////////////////////////////////////////
453/// Check if started process(es) establish connection. After timeout such processed will be killed
454/// Method invoked from http server thread, therefore appropriate mutex must be used on all relevant data
455
457{
458 if (!fMgr) return;
459
460 timestamp_t stamp = std::chrono::system_clock::now();
461
462 float tmout = fMgr->GetLaunchTmout();
463
464 ConnectionsList_t selected;
465
466 {
467 std::lock_guard<std::mutex> grd(fConnMutex);
468
469 auto pred = [&](std::shared_ptr<WebConn> &e) {
470 std::chrono::duration<double> diff = stamp - e->fSendStamp;
471
472 if (diff.count() > tmout) {
473 R__DEBUG_HERE("webgui") << "Halt process after " << diff.count() << " sec";
474 selected.emplace_back(e);
475 return true;
476 }
477
478 return false;
479 };
480
481 fPendingConn.erase(std::remove_if(fPendingConn.begin(), fPendingConn.end(), pred), fPendingConn.end());
482 }
483
484}
485
486
487//////////////////////////////////////////////////////////////////////////////////////////
488/// Check if there are connection which are inactive for longer time
489/// For instance, batch browser will be stopped if no activity for 30 sec is there
490
492{
493 timestamp_t stamp = std::chrono::system_clock::now();
494
495 double batch_tmout = 20.;
496
497 std::vector<std::shared_ptr<WebConn>> clr;
498
499 {
500 std::lock_guard<std::mutex> grd(fConnMutex);
501
502 auto pred = [&](std::shared_ptr<WebConn> &conn) {
503 std::chrono::duration<double> diff = stamp - conn->fSendStamp;
504 // introduce large timeout
505 if ((diff.count() > batch_tmout) && conn->fBatchMode) {
506 conn->fActive = false;
507 clr.emplace_back(conn);
508 return true;
509 }
510 return false;
511 };
512
513 fConn.erase(std::remove_if(fConn.begin(), fConn.end(), pred), fConn.end());
514 }
515
516 for (auto &entry : clr)
517 ProvideQueueEntry(entry->fConnId, kind_Disconnect, ""s);
518
519}
520
521//////////////////////////////////////////////////////////////////////////////////////////
522/// Processing of websockets call-backs, invoked from RWebWindowWSHandler
523/// Method invoked from http server thread, therefore appropriate mutex must be used on all relevant data
524
526{
527 if (arg.GetWSId() == 0)
528 return true;
529
530 if (arg.IsMethod("WS_CONNECT")) {
531
532 std::lock_guard<std::mutex> grd(fConnMutex);
533
534 // refuse connection when number of connections exceed limit
535 if (fConnLimit && (fConn.size() >= fConnLimit))
536 return false;
537
538 return true;
539 }
540
541 if (arg.IsMethod("WS_READY")) {
542
543 auto conn = FindOrCreateConnection(arg.GetWSId(), true, arg.GetQuery());
544
545 if (conn) {
546 R__ERROR_HERE("webgui") << "WSHandle with given websocket id " << arg.GetWSId() << " already exists";
547 return false;
548 }
549
550 return true;
551 }
552
553 if (arg.IsMethod("WS_CLOSE")) {
554 // connection is closed, one can remove handle, associated window will be closed
555
556 auto conn = RemoveConnection(arg.GetWSId());
557
558 if (conn)
559 ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
560
561 return true;
562 }
563
564 if (!arg.IsMethod("WS_DATA")) {
565 R__ERROR_HERE("webgui") << "only WS_DATA request expected!";
566 return false;
567 }
568
569 auto conn = FindConnection(arg.GetWSId());
570
571 if (!conn) {
572 R__ERROR_HERE("webgui") << "Get websocket data without valid connection - ignore!!!";
573 return false;
574 }
575
576 if (arg.GetPostDataLength() <= 0)
577 return true;
578
579 // here processing of received data should be performed
580 // this is task for the implemented windows
581
582 const char *buf = (const char *)arg.GetPostData();
583 char *str_end = nullptr;
584
585 unsigned long ackn_oper = std::strtoul(buf, &str_end, 10);
586 if (!str_end || *str_end != ':') {
587 R__ERROR_HERE("webgui") << "missing number of acknowledged operations";
588 return false;
589 }
590
591 unsigned long can_send = std::strtoul(str_end + 1, &str_end, 10);
592 if (!str_end || *str_end != ':') {
593 R__ERROR_HERE("webgui") << "missing can_send counter";
594 return false;
595 }
596
597 unsigned long nchannel = std::strtoul(str_end + 1, &str_end, 10);
598 if (!str_end || *str_end != ':') {
599 R__ERROR_HERE("webgui") << "missing channel number";
600 return false;
601 }
602
603 Long_t processed_len = (str_end + 1 - buf);
604
605 if (processed_len > arg.GetPostDataLength()) {
606 R__ERROR_HERE("webgui") << "corrupted buffer";
607 return false;
608 }
609
610 std::string cdata(str_end + 1, arg.GetPostDataLength() - processed_len);
611
612 timestamp_t stamp = std::chrono::system_clock::now();
613
614 {
615 std::lock_guard<std::mutex> grd(conn->fMutex);
616
617 conn->fSendCredits += ackn_oper;
618 conn->fRecvCount++;
619 conn->fClientCredits = (int)can_send;
620 conn->fRecvStamp = stamp;
621 }
622
623 if (fProtocolCnt >= 0)
624 if (!fProtocolConnId || (conn->fConnId == fProtocolConnId)) {
625 fProtocolConnId = conn->fConnId; // remember connection
626
627 // record send event only for normal channel or very first message via ch0
628 if ((nchannel != 0) || (cdata.find("READY=") == 0)) {
629 if (fProtocol.length() > 2)
630 fProtocol.insert(fProtocol.length() - 1, ",");
631 fProtocol.insert(fProtocol.length() - 1, "\"send\"");
632
633 std::ofstream pfs(fProtocolFileName);
634 pfs.write(fProtocol.c_str(), fProtocol.length());
635 pfs.close();
636 }
637 }
638
639 if (nchannel == 0) {
640 // special system channel
641 if ((cdata.find("READY=") == 0) && !conn->fReady) {
642 std::string key = cdata.substr(6);
643
644 if (key.empty() && IsNativeOnlyConn()) {
645 RemoveConnection(conn->fWSId);
646 return false;
647 }
648
649 if (!key.empty() && !conn->fKey.empty() && (conn->fKey != key)) {
650 R__ERROR_HERE("webgui") << "Key mismatch after established connection " << key << " != " << conn->fKey;
651 RemoveConnection(conn->fWSId);
652 return false;
653 }
654
655 if (!fPanelName.empty()) {
656 // initialization not yet finished, appropriate panel should be started
657 Send(conn->fConnId, "SHOWPANEL:"s + fPanelName);
658 conn->fReady = 5;
659 } else {
660 ProvideQueueEntry(conn->fConnId, kind_Connect, ""s);
661 conn->fReady = 10;
662 }
663 } else if (cdata.compare(0,8,"CLOSECH=") == 0) {
664 int channel = std::stoi(cdata.substr(8));
665 auto iter = conn->fEmbed.find(channel);
666 if (iter != conn->fEmbed.end()) {
667 iter->second->ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
668 conn->fEmbed.erase(iter);
669 }
670 }
671 } else if (fPanelName.length() && (conn->fReady < 10)) {
672 if (cdata == "PANEL_READY") {
673 R__DEBUG_HERE("webgui") << "Get panel ready " << fPanelName;
674 ProvideQueueEntry(conn->fConnId, kind_Connect, ""s);
675 conn->fReady = 10;
676 } else {
677 ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
678 RemoveConnection(conn->fWSId);
679 }
680 } else if (nchannel == 1) {
681 ProvideQueueEntry(conn->fConnId, kind_Data, std::move(cdata));
682 } else if (nchannel > 1) {
683 // process embed window
684 auto embed_window = conn->fEmbed[nchannel];
685 if (embed_window)
686 embed_window->ProvideQueueEntry(conn->fConnId, kind_Data, std::move(cdata));
687 }
688
690
691 return true;
692}
693
695{
696 auto conn = FindConnection(wsid);
697
698 if (!conn)
699 return;
700
701 {
702 std::lock_guard<std::mutex> grd(conn->fMutex);
703 conn->fDoingSend = false;
704 }
705
706 CheckDataToSend(conn);
707}
708
709//////////////////////////////////////////////////////////////////////////////////////////
710/// Prepare text part of send data
711/// Should be called under locked connection mutex
712
713std::string ROOT::Experimental::RWebWindow::_MakeSendHeader(std::shared_ptr<WebConn> &conn, bool txt, const std::string &data, int chid)
714{
715 std::string buf;
716
717 if (!conn->fWSId || !fWSHandler) {
718 R__ERROR_HERE("webgui") << "try to send text data when connection not established";
719 return buf;
720 }
721
722 if (conn->fSendCredits <= 0) {
723 R__ERROR_HERE("webgui") << "No credits to send text data via connection";
724 return buf;
725 }
726
727 if (conn->fDoingSend) {
728 R__ERROR_HERE("webgui") << "Previous send operation not completed yet";
729 return buf;
730 }
731
732 if (txt)
733 buf.reserve(data.length() + 100);
734
735 buf.append(std::to_string(conn->fRecvCount));
736 buf.append(":");
737 buf.append(std::to_string(conn->fSendCredits));
738 buf.append(":");
739 conn->fRecvCount = 0; // we confirm how many packages was received
740 conn->fSendCredits--;
741
742 buf.append(std::to_string(chid));
743 buf.append(":");
744
745 if (txt) {
746 buf.append(data);
747 } else if (data.length()==0) {
748 buf.append("$$nullbinary$$");
749 } else {
750 buf.append("$$binary$$");
751 }
752
753 return buf;
754}
755
756//////////////////////////////////////////////////////////////////////////////////////////
757/// Checks if one should send data for specified connection
758/// Returns true when send operation was performed
759
760bool ROOT::Experimental::RWebWindow::CheckDataToSend(std::shared_ptr<WebConn> &conn)
761{
762 std::string hdr, data;
763
764 {
765 std::lock_guard<std::mutex> grd(conn->fMutex);
766
767 if (!conn->fActive || (conn->fSendCredits <= 0) || conn->fDoingSend) return false;
768
769 if (!conn->fQueue.empty()) {
770 QueueItem &item = conn->fQueue.front();
771 hdr = _MakeSendHeader(conn, item.fText, item.fData, item.fChID);
772 if (!hdr.empty() && !item.fText)
773 data = std::move(item.fData);
774 conn->fQueue.pop();
775 } else if ((conn->fClientCredits < 3) && (conn->fRecvCount > 1)) {
776 // give more credits to the client
777 hdr = _MakeSendHeader(conn, true, "KEEPALIVE", 0);
778 }
779
780 if (hdr.empty()) return false;
781
782 conn->fDoingSend = true;
783 }
784
785 int res = 0;
786
787 if (data.empty()) {
788 res = fWSHandler->SendCharStarWS(conn->fWSId, hdr.c_str());
789 } else {
790 res = fWSHandler->SendHeaderWS(conn->fWSId, hdr.c_str(), data.data(), data.length());
791 }
792
793 // submit operation, will be processed
794 if (res >=0) return true;
795
796
797 // failure, clear sending flag
798 std::lock_guard<std::mutex> grd(conn->fMutex);
799 conn->fDoingSend = false;
800 return false;
801}
802
803
804//////////////////////////////////////////////////////////////////////////////////////////
805/// Checks if new data can be send (internal use only)
806/// If necessary, provide credits to the client
807
809{
810 // make copy of all connections to be independent later, only active connections are checked
811 auto arr = GetConnections(0, true);
812
813 do {
814 bool isany = false;
815
816 for (auto &conn : arr)
817 if (CheckDataToSend(conn))
818 isany = true;
819
820 if (!isany) break;
821
822 } while (!only_once);
823}
824
825///////////////////////////////////////////////////////////////////////////////////
826/// Special method to process all internal activity when window runs in separate thread
827
829{
831
833
835
837}
838
839///////////////////////////////////////////////////////////////////////////////////
840/// Returns window address which is used in URL
841
843{
844 return fWSHandler->GetName();
845}
846
847///////////////////////////////////////////////////////////////////////////////////
848/// Returns relative URL address for the specified window
849/// Address can be required if one needs to access data from one window into another window
850/// Used for instance when inserting panel into canvas
851
852std::string ROOT::Experimental::RWebWindow::GetRelativeAddr(const std::shared_ptr<RWebWindow> &win) const
853{
854 if (fMgr != win->fMgr) {
855 R__ERROR_HERE("WebDisplay") << "Same web window manager should be used";
856 return "";
857 }
858
859 std::string res("../");
860 res.append(win->GetAddr());
861 res.append("/");
862 return res;
863}
864
865/////////////////////////////////////////////////////////////////////////
866/// Set client version, used as prefix in scripts URL
867/// When changed, web browser will reload all related JS files while full URL will be different
868/// Default is empty value - no extra string in URL
869/// Version should be string like "1.2" or "ver1.subv2" and not contain any special symbols
870
872{
873 std::lock_guard<std::mutex> grd(fConnMutex);
874 fClientVersion = vers;
875}
876
877/////////////////////////////////////////////////////////////////////////
878/// Returns current client version
879
881{
882 std::lock_guard<std::mutex> grd(fConnMutex);
883 return fClientVersion;
884}
885
886/////////////////////////////////////////////////////////////////////////
887/// Set arbitrary JSON code, which is accessible via conn.GetUserArgs() method
888/// This JSON code injected into main HTML document into JSROOT.ConnectWebWindow()
889/// Must be called before RWebWindow::Show() method is called
890
892{
893 std::lock_guard<std::mutex> grd(fConnMutex);
894 fUserArgs = args;
895}
896
897/////////////////////////////////////////////////////////////////////////
898/// Returns configured user arguments for web window
899/// See \ref SetUserArgs method for more details
900
902{
903 std::lock_guard<std::mutex> grd(fConnMutex);
904 return fUserArgs;
905}
906
907///////////////////////////////////////////////////////////////////////////////////
908/// Returns current number of active clients connections
909
911{
912 std::lock_guard<std::mutex> grd(fConnMutex);
913 auto sz = fConn.size();
914 if (with_pending)
915 sz += fPendingConn.size();
916 return sz;
917}
918
919///////////////////////////////////////////////////////////////////////////////////
920/// Configures recording of communication data in protocol file
921/// Provided filename will be used to store JSON array with names of written files - text or binary
922/// If data was send from client, "send" entry will be placed. JSON file will look like:
923/// ["send","msg0.txt","send","msg1.txt","msg2.txt"]
924/// If empty file name is provided, data recording will be disabled
925/// Recorded data can be used in JSROOT directly to test client code without running C++ server
926
927void ROOT::Experimental::RWebWindow::RecordData(const std::string &fname, const std::string &fprefix)
928{
929 fProtocolFileName = fname;
930 fProtocolCnt = fProtocolFileName.empty() ? -1 : 0;
932 fProtocolPrefix = fprefix;
933 fProtocol = "[]"; // empty array
934}
935
936///////////////////////////////////////////////////////////////////////////////////
937/// Returns connection for specified connection number
938/// Only active connections are returned - where clients confirms connection
939/// Total number of connections can be retrieved with NumConnections() method
940
942{
943 std::lock_guard<std::mutex> grd(fConnMutex);
944 return ((num >= 0) && (num < (int)fConn.size()) && fConn[num]->fActive) ? fConn[num]->fConnId : 0;
945}
946
947///////////////////////////////////////////////////////////////////////////////////
948/// returns true if specified connection id exists
949/// connid is connection (0 - any)
950/// if only_active==false, also inactive connections check or connections which should appear
951
952bool ROOT::Experimental::RWebWindow::HasConnection(unsigned connid, bool only_active) const
953{
954 std::lock_guard<std::mutex> grd(fConnMutex);
955
956 for (auto &conn : fConn) {
957 if (connid && (conn->fConnId != connid))
958 continue;
959 if (conn->fActive || !only_active)
960 return true;
961 }
962
963 if (!only_active)
964 for (auto &conn : fPendingConn) {
965 if (!connid || (conn->fConnId == connid))
966 return true;
967 }
968
969 return false;
970}
971
972///////////////////////////////////////////////////////////////////////////////////
973/// Closes all connection to clients
974/// Normally leads to closing of all correspondent browser windows
975/// Some browsers (like firefox) do not allow by default to close window
976
978{
979 SubmitData(0, true, "CLOSE", 0);
980}
981
982///////////////////////////////////////////////////////////////////////////////////
983/// Close specified connection
984/// Connection id usually appears in the correspondent call-backs
985
987{
988 if (connid)
989 SubmitData(connid, true, "CLOSE", 0);
990}
991
992///////////////////////////////////////////////////////////////////////////////////
993/// returns connection (or all active connections)
994
996{
998
999 {
1000 std::lock_guard<std::mutex> grd(fConnMutex);
1001
1002 for (auto &conn : fConn) {
1003 if ((conn->fActive || !only_active) && (!connid || (conn->fConnId == connid)))
1004 arr.push_back(conn);
1005 }
1006
1007 if (!only_active)
1008 for (auto &conn : fPendingConn)
1009 if (!connid || (conn->fConnId == connid))
1010 arr.push_back(conn);
1011 }
1012
1013 return arr;
1014}
1015
1016///////////////////////////////////////////////////////////////////////////////////
1017/// returns true if sending via specified connection can be performed
1018/// if direct==true, checks if direct sending (without queuing) is possible
1019/// if connid==0, all existing connections are checked
1020
1021bool ROOT::Experimental::RWebWindow::CanSend(unsigned connid, bool direct) const
1022{
1023 auto arr = GetConnections(connid, direct); // for direct sending connection has to be active
1024
1025 auto maxqlen = GetMaxQueueLength();
1026
1027 for (auto &conn : arr) {
1028
1029 std::lock_guard<std::mutex> grd(conn->fMutex);
1030
1031 if (direct && (!conn->fQueue.empty() || (conn->fSendCredits == 0) || conn->fDoingSend))
1032 return false;
1033
1034 if (conn->fQueue.size() >= maxqlen)
1035 return false;
1036 }
1037
1038 return true;
1039}
1040
1041///////////////////////////////////////////////////////////////////////////////////
1042/// returns send queue length for specified connection
1043/// if connid==0, maximal value for all connections is returned
1044/// If wrong connection is specified, -1 is return
1045
1047{
1048 int maxq = -1;
1049
1050 for (auto &conn : GetConnections(connid)) {
1051 std::lock_guard<std::mutex> grd(conn->fMutex);
1052 int len = conn->fQueue.size();
1053 if (len > maxq) maxq = len;
1054 }
1055
1056 return maxq;
1057}
1058
1059
1060///////////////////////////////////////////////////////////////////////////////////
1061/// Internal method to send data
1062/// Allows to specify channel. chid==1 is normal communication, chid==0 for internal with higher priority
1063/// If connid==0, data will be send to all connections
1064
1065void ROOT::Experimental::RWebWindow::SubmitData(unsigned connid, bool txt, std::string &&data, int chid)
1066{
1067 if (fMaster)
1068 return fMaster->SubmitData(fMasterConnId, txt, std::move(data), fMasterChannel);
1069
1070 auto arr = GetConnections(connid);
1071 auto cnt = arr.size();
1072 auto maxqlen = GetMaxQueueLength();
1073
1074 timestamp_t stamp = std::chrono::system_clock::now();
1075
1076 for (auto &conn : arr) {
1077
1078 if (fProtocolCnt >= 0)
1079 if (!fProtocolConnId || (conn->fConnId == fProtocolConnId)) {
1080 fProtocolConnId = conn->fConnId; // remember connection
1081 std::string fname = fProtocolPrefix;
1082 fname.append("msg");
1083 fname.append(std::to_string(fProtocolCnt++));
1084 fname.append(txt ? ".txt" : ".bin");
1085
1086 std::ofstream ofs(fname);
1087 ofs.write(data.c_str(), data.length());
1088 ofs.close();
1089
1090 if (fProtocol.length() > 2)
1091 fProtocol.insert(fProtocol.length() - 1, ",");
1092 fProtocol.insert(fProtocol.length() - 1, "\""s + fname + "\""s);
1093
1094 std::ofstream pfs(fProtocolFileName);
1095 pfs.write(fProtocol.c_str(), fProtocol.length());
1096 pfs.close();
1097 }
1098
1099 conn->fSendStamp = stamp;
1100
1101 std::lock_guard<std::mutex> grd(conn->fMutex);
1102
1103 if (conn->fQueue.size() < maxqlen) {
1104 if (--cnt)
1105 conn->fQueue.emplace(chid, txt, std::string(data)); // make copy
1106 else
1107 conn->fQueue.emplace(chid, txt, std::move(data)); // move content
1108 } else {
1109 R__ERROR_HERE("webgui") << "Maximum queue length achieved";
1110 }
1111 }
1112
1114}
1115
1116///////////////////////////////////////////////////////////////////////////////////
1117/// Sends data to specified connection
1118/// If connid==0, data will be send to all connections
1119
1120void ROOT::Experimental::RWebWindow::Send(unsigned connid, const std::string &data)
1121{
1122 SubmitData(connid, true, std::string(data), 1);
1123}
1124
1125///////////////////////////////////////////////////////////////////////////////////
1126/// Send binary data to specified connection
1127/// If connid==0, data will be sent to all connections
1128
1129void ROOT::Experimental::RWebWindow::SendBinary(unsigned connid, std::string &&data)
1130{
1131 SubmitData(connid, false, std::move(data), 1);
1132}
1133
1134///////////////////////////////////////////////////////////////////////////////////
1135/// Send binary data to specified connection
1136/// If connid==0, data will be sent to all connections
1137
1138void ROOT::Experimental::RWebWindow::SendBinary(unsigned connid, const void *data, std::size_t len)
1139{
1140 std::string buf;
1141 buf.resize(len);
1142 std::copy((const char *)data, (const char *)data + len, buf.begin());
1143 SubmitData(connid, false, std::move(buf), 1);
1144}
1145
1146///////////////////////////////////////////////////////////////////////////////////
1147/// Assign thread id which has to be used for callbacks
1148
1150{
1151 fCallbacksThrdIdSet = true;
1152 fCallbacksThrdId = std::this_thread::get_id();
1154 fProcessMT = true;
1155 } else if (fMgr->IsUseHttpThread()) {
1156 // special thread is used by the manager, but main thread used for the canvas - not supported
1157 R__ERROR_HERE("webgui") << "create web window from main thread when THttpServer created with special thread - not supported";
1158 }
1159}
1160
1161/////////////////////////////////////////////////////////////////////////////////
1162/// Set call-back function for data, received from the clients via websocket
1163///
1164/// Function should have signature like void func(unsigned connid, const std::string &data)
1165/// First argument identifies connection (unique for each window), second argument is received data
1166///
1167/// At the moment when callback is assigned, RWebWindow working thread is detected.
1168/// If called not from main application thread, RWebWindow::Run() function must be regularly called from that thread.
1169///
1170/// Most simple way to assign call-back - use of c++11 lambdas like:
1171/// ~~~ {.cpp}
1172/// auto win = RWebWindow::Create();
1173/// win->SetDefaultPage("file:./page.htm");
1174/// win->SetDataCallBack(
1175/// [](unsigned connid, const std::string &data) {
1176/// printf("Conn:%u data:%s\n", connid, data.c_str());
1177/// }
1178/// );
1179/// win->Show();
1180/// ~~~
1181
1183{
1185 fDataCallback = func;
1186}
1187
1188/////////////////////////////////////////////////////////////////////////////////
1189/// Set call-back function for new connection
1190
1192{
1194 fConnCallback = func;
1195}
1196
1197/////////////////////////////////////////////////////////////////////////////////
1198/// Set call-back function for disconnecting
1199
1201{
1203 fDisconnCallback = func;
1204}
1205
1206/////////////////////////////////////////////////////////////////////////////////
1207/// Set call-backs function for connect, data and disconnect events
1208
1210{
1212 fConnCallback = conn;
1213 fDataCallback = data;
1214 fDisconnCallback = disconn;
1215}
1216
1217/////////////////////////////////////////////////////////////////////////////////
1218/// Waits until provided check function or lambdas returns non-zero value
1219/// Check function has following signature: int func(double spent_tm)
1220/// Waiting will be continued, if function returns zero.
1221/// Parameter spent_tm is time in seconds, which already spent inside the function
1222/// First non-zero value breaks loop and result is returned.
1223/// Runs application mainloop and short sleeps in-between
1224
1226{
1227 return fMgr->WaitFor(*this, check);
1228}
1229
1230/////////////////////////////////////////////////////////////////////////////////
1231/// Waits until provided check function or lambdas returns non-zero value
1232/// Check function has following signature: int func(double spent_tm)
1233/// Waiting will be continued, if function returns zero.
1234/// Parameter spent_tm in lambda is time in seconds, which already spent inside the function
1235/// First non-zero value breaks waiting loop and result is returned (or 0 if time is expired).
1236/// Runs application mainloop and short sleeps in-between
1237/// WebGui.OperationTmout rootrc parameter defines waiting time in seconds
1238
1240{
1241 return fMgr->WaitFor(*this, check, true, GetOperationTmout());
1242}
1243
1244/////////////////////////////////////////////////////////////////////////////////
1245/// Waits until provided check function or lambdas returns non-zero value
1246/// Check function has following signature: int func(double spent_tm)
1247/// Waiting will be continued, if function returns zero.
1248/// Parameter spent_tm in lambda is time in seconds, which already spent inside the function
1249/// First non-zero value breaks waiting loop and result is returned (or 0 if time is expired).
1250/// Runs application mainloop and short sleeps in-between
1251/// duration (in seconds) defines waiting time
1252
1254{
1255 return fMgr->WaitFor(*this, check, true, duration);
1256}
1257
1258
1259/////////////////////////////////////////////////////////////////////////////////
1260/// Run window functionality for specified time
1261/// If no action can be performed - just sleep specified time
1262
1264{
1265 if (!fCallbacksThrdIdSet || (fCallbacksThrdId != std::this_thread::get_id())) {
1266 R__WARNING_HERE("webgui") << "Change thread id where RWebWindow is executed";
1267 fCallbacksThrdIdSet = true;
1268 fCallbacksThrdId = std::this_thread::get_id();
1269 }
1270
1271 if (tm <= 0) {
1272 Sync();
1273 } else {
1274 WaitForTimed([](double) { return 0; }, tm);
1275 }
1276}
1277
1278
1279/////////////////////////////////////////////////////////////////////////////////
1280/// Add embed window
1281
1282unsigned ROOT::Experimental::RWebWindow::AddEmbedWindow(std::shared_ptr<RWebWindow> window, int channel)
1283{
1284 if (channel < 2)
1285 return 0;
1286
1287 auto arr = GetConnections(0, true);
1288 if (arr.size() == 0)
1289 return 0;
1290
1291 // check if channel already occupied
1292 if (arr[0]->fEmbed.find(channel) != arr[0]->fEmbed.end())
1293 return 0;
1294
1295 arr[0]->fEmbed[channel] = window;
1296
1297 return arr[0]->fConnId;
1298}
1299
1300/////////////////////////////////////////////////////////////////////////////////
1301/// Remove RWebWindow associated with the channel
1302
1304{
1305 auto arr = GetConnections(connid);
1306
1307 for (auto &conn : arr) {
1308 auto iter = conn->fEmbed.find(channel);
1309 if (iter != conn->fEmbed.end())
1310 conn->fEmbed.erase(iter);
1311 }
1312}
1313
1314
1315/////////////////////////////////////////////////////////////////////////////////
1316/// Create new RWebWindow
1317/// Using default RWebWindowsManager
1318
1319std::shared_ptr<ROOT::Experimental::RWebWindow> ROOT::Experimental::RWebWindow::Create()
1320{
1321 return ROOT::Experimental::RWebWindowsManager::Instance()->CreateWindow();
1322}
1323
1324/////////////////////////////////////////////////////////////////////////////////
1325/// Terminate ROOT session
1326/// Tries to correctly close THttpServer, associated with RWebWindowsManager
1327/// After that exit from process
1328
1330{
1331 fMgr->Terminate();
1332}
1333
1334/////////////////////////////////////////////////////////////////////////////////
1335/// Static method to show web window
1336/// Has to be used instead of RWebWindow::Show() when window potentially can be embed into other windows
1337/// Soon RWebWindow::Show() method will be done protected
1338
1339unsigned ROOT::Experimental::RWebWindow::ShowWindow(std::shared_ptr<RWebWindow> window, const RWebDisplayArgs &args)
1340{
1341 if (!window)
1342 return 0;
1343
1345 unsigned connid = args.fMaster ? args.fMaster->AddEmbedWindow(window, args.fMasterChannel) : 0;
1346
1347 if (connid > 0) {
1348 window->fMaster = args.fMaster;
1349 window->fMasterConnId = connid;
1350 window->fMasterChannel = args.fMasterChannel;
1351
1352 // inform client that connection is established and window initialized
1353 args.fMaster->SubmitData(connid, true, "EMBED_DONE"s, args.fMasterChannel);
1354
1355 // provide call back for window itself that connection is ready
1356 window->ProvideQueueEntry(connid, kind_Connect, ""s);
1357 }
1358
1359 return connid;
1360 }
1361
1362 return window->Show(args);
1363}
1364
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
#define R__WARNING_HERE(GROUP)
Definition: RLogger.hxx:184
#define R__DEBUG_HERE(GROUP)
Definition: RLogger.hxx:186
#define e(i)
Definition: RSha256.hxx:103
long Long_t
Definition: RtypesCore.h:52
XFontStruct * id
Definition: TGX11.cxx:108
char name[80]
Definition: TGX11.cxx:109
char * Form(const char *fmt,...)
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
int fMasterChannel
! used master channel
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
std::shared_ptr< RWebWindow > fMaster
! master window
@ kEmbedded
window will be embedded into other, no extra browser need to be started
std::vector< std::shared_ptr< WebConn > > ConnectionsList_t
Definition: RWebWindow.hxx:113
void RemoveEmbedWindow(unsigned connid, int channel)
Remove RWebWindow associated with the channel.
std::shared_ptr< RWebWindow > fMaster
! master window where this window is embeded
Definition: RWebWindow.hxx:116
int WaitForTimed(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
std::shared_ptr< WebConn > FindOrCreateConnection(unsigned wsid, bool make_new, const char *query)
Find connection with given websocket id Connection mutex should be locked before method calling.
Definition: RWebWindow.cxx:235
std::string fUserArgs
! arbitrary JSON code, which is accessible via conn.getUserArgs() method
Definition: RWebWindow.hxx:148
bool CheckDataToSend(std::shared_ptr< WebConn > &conn)
Checks if one should send data for specified connection Returns true when send operation was performe...
Definition: RWebWindow.cxx:760
int fMasterChannel
! channel id in the master window
Definition: RWebWindow.hxx:118
float GetOperationTmout() const
Returns timeout for synchronous WebWindow operations.
Definition: RWebWindow.hxx:270
bool ProcessWS(THttpCallArg &arg)
Processing of websockets call-backs, invoked from RWebWindowWSHandler Method invoked from http server...
Definition: RWebWindow.cxx:525
std::shared_ptr< WebConn > FindConnection(unsigned wsid)
Definition: RWebWindow.hxx:160
void SetDefaultPage(const std::string &page)
Set content of default window HTML page This page returns when URL address of the window will be requ...
Definition: RWebWindow.hxx:205
void Run(double tm=0.)
Run window functionality for specified time If no action can be performed - just sleep specified time...
void SetUserArgs(const std::string &args)
Set arbitrary JSON code, which is accessible via conn.GetUserArgs() method This JSON code injected in...
Definition: RWebWindow.cxx:891
unsigned GetId() const
Returns ID for the window - unique inside window manager.
Definition: RWebWindow.hxx:199
void CloseConnection(unsigned connid)
Close specified connection Connection id usually appears in the correspondent call-backs.
Definition: RWebWindow.cxx:986
void CheckInactiveConnections()
Check if there are connection which are inactive for longer time For instance, batch browser will be ...
Definition: RWebWindow.cxx:491
ConnectionsList_t fConn
! list of all accepted connections
Definition: RWebWindow.hxx:127
unsigned FindBatch()
Returns connection id of batch job Connection to that job may not be initialized yet If connection do...
Definition: RWebWindow.cxx:191
std::string fProtocolPrefix
! prefix for created files names
Definition: RWebWindow.hxx:146
WebWindowConnectCallback_t fConnCallback
! callback for connect event
Definition: RWebWindow.hxx:132
unsigned GetMaxQueueLength() const
Return maximal queue length of data which can be held by window.
Definition: RWebWindow.hxx:241
bool HasKey(const std::string &key) const
Returns true if provided key value already exists (in processes map or in existing connections)
Definition: RWebWindow.cxx:435
void SetDisconnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for disconnecting.
unsigned fConnLimit
! number of allowed active connections
Definition: RWebWindow.hxx:129
bool fCallbacksThrdIdSet
! flag indicating that thread id is assigned
Definition: RWebWindow.hxx:136
unsigned Show(const RWebDisplayArgs &args="")
Show window in specified location See ROOT::Experimental::RWebWindowsManager::Show() docu for more in...
Definition: RWebWindow.cxx:165
std::shared_ptr< RWebWindowWSHandler > CreateWSHandler(std::shared_ptr< RWebWindowsManager > mgr, unsigned id, double tmout)
Assigns manager reference, window id and creates websocket handler, used for communication with the c...
Definition: RWebWindow.cxx:131
void Send(unsigned connid, const std::string &data)
Sends data to specified connection If connid==0, data will be send to all connections.
std::string _MakeSendHeader(std::shared_ptr< WebConn > &conn, bool txt, const std::string &data, int chid)
Prepare text part of send data Should be called under locked connection mutex.
Definition: RWebWindow.cxx:713
unsigned GetConnectionId(int num=0) const
Returns connection for specified connection number Only active connections are returned - where clien...
Definition: RWebWindow.cxx:941
unsigned fMasterConnId
! master connection id
Definition: RWebWindow.hxx:117
bool fSendMT
! true is special threads should be used for sending data
Definition: RWebWindow.hxx:123
int WaitFor(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
std::thread::id fCallbacksThrdId
! thread id where callbacks should be invoked
Definition: RWebWindow.hxx:135
std::chrono::time_point< std::chrono::system_clock > timestamp_t
Definition: RWebWindow.hxx:63
std::string fClientVersion
! configured client version, used as prefix in scripts URL
Definition: RWebWindow.hxx:142
void CloseConnections()
Closes all connection to clients Normally leads to closing of all correspondent browser windows Some ...
Definition: RWebWindow.cxx:977
unsigned AddDisplayHandle(bool batch_mode, const std::string &key, std::unique_ptr< RWebDisplayHandle > &handle)
Add display handle and associated key Key is random number generated when starting new window When cl...
Definition: RWebWindow.cxx:417
unsigned fConnCnt
! counter of new connections to assign ids
Definition: RWebWindow.hxx:125
THttpServer * GetServer()
Return THttpServer instance serving requests to the window.
Definition: RWebWindow.cxx:155
std::string fPanelName
! panel name which should be shown in the window
Definition: RWebWindow.hxx:120
unsigned fProtocolConnId
! connection id, which is used for writing protocol
Definition: RWebWindow.hxx:145
void TerminateROOT()
Terminate ROOT session Tries to correctly close THttpServer, associated with RWebWindowsManager After...
void SetDataCallBack(WebWindowDataCallback_t func)
Set call-back function for data, received from the clients via websocket.
std::string GetUserArgs() const
Returns configured user arguments for web window See SetUserArgs method for more details.
Definition: RWebWindow.cxx:901
WebWindowDataCallback_t fDataCallback
! main callback when data over channel 1 is arrived
Definition: RWebWindow.hxx:133
void Sync()
Special method to process all internal activity when window runs in separate thread.
Definition: RWebWindow.cxx:828
ConnectionsList_t fPendingConn
! list of pending connection with pre-assigned keys
Definition: RWebWindow.hxx:126
void InvokeCallbacks(bool force=false)
Invoke callbacks with existing data Must be called from appropriate thread.
Definition: RWebWindow.cxx:373
std::string GetUrl(bool remote=true)
Return URL string to access web window If remote flag is specified, real HTTP server will be started ...
Definition: RWebWindow.cxx:147
bool HasConnection(unsigned connid=0, bool only_active=true) const
returns true if specified connection id exists connid is connection (0 - any) if only_active==false,...
Definition: RWebWindow.cxx:952
void RecordData(const std::string &fname="protocol.json", const std::string &fprefix="")
Configures recording of communication data in protocol file Provided filename will be used to store J...
Definition: RWebWindow.cxx:927
unsigned GetDisplayConnection() const
Returns first connection id where window is displayed It could be that connection(s) not yet fully es...
Definition: RWebWindow.cxx:214
void SetCallBacks(WebWindowConnectCallback_t conn, WebWindowDataCallback_t data, WebWindowConnectCallback_t disconn=nullptr)
Set call-backs function for connect, data and disconnect events.
std::string GetAddr() const
Returns window address which is used in URL.
Definition: RWebWindow.cxx:842
int GetSendQueueLength(unsigned connid) const
returns send queue length for specified connection if connid==0, maximal value for all connections is...
void ProvideQueueEntry(unsigned connid, EQueueEntryKind kind, std::string &&arg)
Provide data to user callback User callback must be executed in the window thread.
Definition: RWebWindow.cxx:359
void AssignCallbackThreadId()
Assign thread id which has to be used for callbacks.
RWebWindow()
RWebWindow constructor Should be defined here because of std::unique_ptr<RWebWindowWSHandler>
std::shared_ptr< RWebWindowWSHandler > fWSHandler
! specialize websocket handler for all incoming connections
Definition: RWebWindow.hxx:124
std::string fProtocolFileName
! local file where communication protocol will be written
Definition: RWebWindow.hxx:143
bool CanSend(unsigned connid, bool direct=true) const
returns true if sending via specified connection can be performed if direct==true,...
std::string GetRelativeAddr(const std::shared_ptr< RWebWindow > &win) const
Returns relative URL address for the specified window Address can be required if one needs to access ...
Definition: RWebWindow.cxx:852
void SetPanelName(const std::string &name)
Configure window to show some of existing JSROOT panels It uses "file:rootui5sys/panel/panel....
Definition: RWebWindow.cxx:113
void SubmitData(unsigned connid, bool txt, std::string &&data, int chid=1)
Internal method to send data Allows to specify channel.
~RWebWindow()
RWebWindow destructor Closes all connections and remove window from manager.
Definition: RWebWindow.cxx:78
void SetClientVersion(const std::string &vers)
Set client version, used as prefix in scripts URL When changed, web browser will reload all related J...
Definition: RWebWindow.cxx:871
std::shared_ptr< RWebWindowsManager > fMgr
! display manager
Definition: RWebWindow.hxx:115
bool ProcessBatchHolder(std::shared_ptr< THttpCallArg > &arg)
Process special http request, used to hold headless browser running Such requests should not be repli...
Definition: RWebWindow.cxx:311
unsigned AddEmbedWindow(std::shared_ptr< RWebWindow > window, int channel)
Add embed window.
void CompleteWSSend(unsigned wsid)
Definition: RWebWindow.cxx:694
std::mutex fInputQueueMutex
! mutex to protect input queue
Definition: RWebWindow.hxx:138
void SetConnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for new connection.
ConnectionsList_t GetConnections(unsigned connid=0, bool only_active=false) const
returns connection (or all active connections)
Definition: RWebWindow.cxx:995
bool IsNativeOnlyConn() const
returns true if only native (own-created) connections are allowed
Definition: RWebWindow.hxx:249
int fProtocolCnt
! counter for protocol recording
Definition: RWebWindow.hxx:144
std::queue< QueueEntry > fInputQueue
! input queue for all callbacks
Definition: RWebWindow.hxx:137
void SendBinary(unsigned connid, const void *data, std::size_t len)
Send binary data to specified connection If connid==0, data will be sent to all connections.
bool fProcessMT
! if window event processing performed in dedicated thread
Definition: RWebWindow.hxx:122
std::string fProtocol
! protocol
Definition: RWebWindow.hxx:147
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
std::shared_ptr< WebConn > RemoveConnection(unsigned wsid)
Remove connection with given websocket id.
Definition: RWebWindow.cxx:282
unsigned fId
! unique identifier
Definition: RWebWindow.hxx:121
float fOperationTmout
! timeout in seconds to perform synchronous operation, default 50s
Definition: RWebWindow.hxx:141
WebWindowConnectCallback_t fDisconnCallback
! callback for disconnect event
Definition: RWebWindow.hxx:134
unsigned MakeBatch(bool create_new=false, const RWebDisplayArgs &args="")
Create batch job for specified window Normally only single batch job is used, but many can be created...
Definition: RWebWindow.cxx:176
static unsigned ShowWindow(std::shared_ptr< RWebWindow > window, const RWebDisplayArgs &args="")
Static method to show web window Has to be used instead of RWebWindow::Show() when window potentially...
void CheckPendingConnections()
Check if started process(es) establish connection.
Definition: RWebWindow.cxx:456
std::string GetClientVersion() const
Returns current client version.
Definition: RWebWindow.cxx:880
std::mutex fConnMutex
! mutex used to protect connection list
Definition: RWebWindow.hxx:128
int NumConnections(bool with_pending=false) const
Returns current number of active clients connections.
Definition: RWebWindow.cxx:910
static std::shared_ptr< RWebWindowsManager > & Instance()
Returns default window manager Used to display all standard ROOT elements like TCanvas or TFitPanel.
static bool IsMainThrd()
Returns true when called from main process Main process recognized at the moment when library is load...
UInt_t GetWSId() const
get web-socket id
Definition: THttpCallArg.h:110
const void * GetPostData() const
return pointer on posted with request data
Definition: THttpCallArg.h:137
const char * GetQuery() const
returns request query (string after ? in request URL)
Definition: THttpCallArg.h:152
Long_t GetPostDataLength() const
return length of posted with request data
Definition: THttpCallArg.h:140
Bool_t IsMethod(const char *name) const
returns kTRUE if post method is used
Definition: THttpCallArg.h:131
This class represents a WWW compatible URL.
Definition: TUrl.h:35
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
Definition: TUrl.cxx:651
void SetOptions(const char *opt)
Definition: TUrl.h:89
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
Definition: TUrl.cxx:674
const Int_t n
Definition: legend1.C:16
void swap(RDirectoryEntry &e1, RDirectoryEntry &e2) noexcept
std::function< void(unsigned)> WebWindowConnectCallback_t
function signature for connect/disconnect call-backs argument is connection id
Definition: RWebWindow.hxx:40
std::function< void(unsigned, const std::string &)> WebWindowDataCallback_t
function signature for call-backs from the window clients first argument is connection id,...
Definition: RWebWindow.hxx:44
std::function< int(double)> WebWindowWaitFunc_t
function signature for waiting call-backs Such callback used when calling thread need to waits for so...
Definition: RWebWindow.hxx:51
static constexpr double s
const char * cnt
Definition: TXMLSetup.cxx:74
std::string fData
! text or binary data
Definition: RWebWindow.hxx:68
~WebConn()
Destructor for WebConn Notify special HTTP request which blocks headless browser from exit.
Definition: RWebWindow.cxx:39
std::shared_ptr< THttpCallArg > fHold
! request used to hold headless browser
Definition: RWebWindow.hxx:77