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