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#include "TSystem.h"
23#include "TRandom3.h"
24
25#include <cstring>
26#include <cstdlib>
27#include <utility>
28#include <assert.h>
29#include <algorithm>
30#include <fstream>
31
32using namespace ROOT;
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::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
72RWebWindow::RWebWindow() = default;
73
74//////////////////////////////////////////////////////////////////////////////////////////
75/// RWebWindow destructor
76/// Closes all connections and remove window from manager
77
79{
80 StopThread();
81
82 if (fMaster) {
83 std::vector<MasterConn> lst;
84 {
85 std::lock_guard<std::mutex> grd(fConnMutex);
86 std::swap(lst, fMasterConns);
87 }
88
89 for (auto &entry : lst)
90 fMaster->RemoveEmbedWindow(entry.connid, entry.channel);
91 fMaster.reset();
92 }
93
94 if (fWSHandler)
95 fWSHandler->SetDisabled();
96
97 if (fMgr) {
98
99 // make copy of all connections
100 auto lst = GetWindowConnections();
101
102 {
103 // clear connections vector under mutex
104 std::lock_guard<std::mutex> grd(fConnMutex);
105 fConn.clear();
106 fPendingConn.clear();
107 }
108
109 for (auto &conn : lst) {
110 conn->fActive = false;
111 for (auto &elem: conn->fEmbed)
112 elem.second->RemoveMasterConnection();
113 conn->fEmbed.clear();
114 }
115
116 fMgr->Unregister(*this);
117 }
118}
119
120//////////////////////////////////////////////////////////////////////////////////////////
121/// Configure window to show some of existing JSROOT panels
122/// It uses "file:rootui5sys/panel/panel.html" as default HTML page
123/// At the moment only FitPanel is existing
124
125void RWebWindow::SetPanelName(const std::string &name)
126{
127 {
128 std::lock_guard<std::mutex> grd(fConnMutex);
129 if (!fConn.empty()) {
130 R__LOG_ERROR(WebGUILog()) << "Cannot configure panel when connection exists";
131 return;
132 }
133 }
134
136 SetDefaultPage("file:rootui5sys/panel/panel.html");
137}
138
139//////////////////////////////////////////////////////////////////////////////////////////
140/// Assigns manager reference, window id and creates websocket handler, used for communication with the clients
141
142std::shared_ptr<RWebWindowWSHandler>
143RWebWindow::CreateWSHandler(std::shared_ptr<RWebWindowsManager> mgr, unsigned id, double tmout)
144{
145 fMgr = mgr;
146 fId = id;
147 fOperationTmout = tmout;
148
149 fSendMT = fMgr->IsUseSenderThreads();
150 fWSHandler = std::make_shared<RWebWindowWSHandler>(*this, Form("win%u", GetId()));
151
152 return fWSHandler;
153}
154
155//////////////////////////////////////////////////////////////////////////////////////////
156/// Return URL string to access web window
157/// \param remote if true, real HTTP server will be started automatically
158
159std::string RWebWindow::GetUrl(bool remote)
160{
161 return fMgr->GetUrl(*this, remote);
162}
163
164//////////////////////////////////////////////////////////////////////////////////////////
165/// Return THttpServer instance serving requests to the window
166
168{
169 return fMgr->GetServer();
170}
171
172//////////////////////////////////////////////////////////////////////////////////////////
173/// Show window in specified location
174/// \see ROOT::RWebWindowsManager::Show for more info
175/// \return (future) connection id (or 0 when fails)
176
178{
179 return fMgr->ShowWindow(*this, args);
180}
181
182//////////////////////////////////////////////////////////////////////////////////////////
183/// Start headless browser for specified window
184/// Normally only single instance is used, but many can be created
185/// See ROOT::RWebWindowsManager::Show() docu for more info
186/// returns (future) connection id (or 0 when fails)
187
188unsigned RWebWindow::MakeHeadless(bool create_new)
189{
190 unsigned connid = 0;
191 if (!create_new)
192 connid = FindHeadlessConnection();
193 if (!connid) {
194 RWebDisplayArgs args;
195 args.SetHeadless(true);
196 connid = fMgr->ShowWindow(*this, args);
197 }
198 return connid;
199}
200
201//////////////////////////////////////////////////////////////////////////////////////////
202/// Returns connection id of window running in headless mode
203/// This can be special connection which may run picture production jobs in background
204/// Connection to that job may not be initialized yet
205/// If connection does not exists, returns 0
206
208{
209 std::lock_guard<std::mutex> grd(fConnMutex);
210
211 for (auto &entry : fPendingConn) {
212 if (entry->fHeadlessMode)
213 return entry->fConnId;
214 }
215
216 for (auto &conn : fConn) {
217 if (conn->fHeadlessMode)
218 return conn->fConnId;
219 }
220
221 return 0;
222}
223
224//////////////////////////////////////////////////////////////////////////////////////////
225/// Returns first connection id where window is displayed
226/// It could be that connection(s) not yet fully established - but also not timed out
227/// Batch jobs will be ignored here
228/// Returns 0 if connection not exists
229
231{
232 std::lock_guard<std::mutex> grd(fConnMutex);
233
234 for (auto &entry : fPendingConn) {
235 if (!entry->fHeadlessMode)
236 return entry->fConnId;
237 }
238
239 for (auto &conn : fConn) {
240 if (!conn->fHeadlessMode)
241 return conn->fConnId;
242 }
243
244 return 0;
245}
246
247//////////////////////////////////////////////////////////////////////////////////////////
248/// Find connection with given websocket id
249
250std::shared_ptr<RWebWindow::WebConn> RWebWindow::FindOrCreateConnection(unsigned wsid, bool make_new, const char *query)
251{
252 std::lock_guard<std::mutex> grd(fConnMutex);
253
254 for (auto &conn : fConn) {
255 if (conn->fWSId == wsid)
256 return conn;
257 }
258
259 // put code to create new connection here to stay under same locked mutex
260 if (make_new) {
261 // check if key was registered already
262
263 std::shared_ptr<WebConn> key;
264 std::string keyvalue;
265
266 if (query) {
267 TUrl url;
268 url.SetOptions(query);
269 if (url.HasOption("key"))
270 keyvalue = url.GetValueFromOptions("key");
271 }
272
273 if (!keyvalue.empty())
274 for (size_t n = 0; n < fPendingConn.size(); ++n)
275 if (fPendingConn[n]->fKey == keyvalue) {
276 key = std::move(fPendingConn[n]);
277 fPendingConn.erase(fPendingConn.begin() + n);
278 break;
279 }
280
281 if (key) {
282 key->fWSId = wsid;
283 key->fActive = true;
284 key->ResetStamps(); // TODO: probably, can be moved outside locked area
285 fConn.emplace_back(key);
286 } else {
287 fConn.emplace_back(std::make_shared<WebConn>(++fConnCnt, wsid));
288 }
289 }
290
291 return nullptr;
292}
293
294//////////////////////////////////////////////////////////////////////////////////////////
295/// Remove connection with given websocket id
296
297std::shared_ptr<RWebWindow::WebConn> RWebWindow::RemoveConnection(unsigned wsid)
298{
299
300 std::shared_ptr<WebConn> res;
301
302 {
303 std::lock_guard<std::mutex> grd(fConnMutex);
304
305 for (size_t n = 0; n < fConn.size(); ++n)
306 if (fConn[n]->fWSId == wsid) {
307 res = std::move(fConn[n]);
308 fConn.erase(fConn.begin() + n);
309 res->fActive = false;
310 break;
311 }
312 }
313
314 if (res) {
315 for (auto &elem: res->fEmbed)
316 elem.second->RemoveMasterConnection(res->fConnId);
317 res->fEmbed.clear();
318 }
319
320 return res;
321}
322
323
324//////////////////////////////////////////////////////////////////////////////////////////
325/// Add new master connection
326/// If there are many connections - only same master is allowed
327
328void RWebWindow::AddMasterConnection(std::shared_ptr<RWebWindow> window, unsigned connid, int channel)
329{
330 if (fMaster && fMaster != window)
331 R__LOG_ERROR(WebGUILog()) << "Cannot configure different masters at the same time";
332
333 fMaster = window;
334
335 std::lock_guard<std::mutex> grd(fConnMutex);
336
337 fMasterConns.emplace_back(connid, channel);
338}
339
340//////////////////////////////////////////////////////////////////////////////////////////
341/// Get list of master connections
342
343std::vector<RWebWindow::MasterConn> RWebWindow::GetMasterConnections(unsigned connid) const
344{
345 std::vector<MasterConn> lst;
346 if (!fMaster)
347 return lst;
348
349 std::lock_guard<std::mutex> grd(fConnMutex);
350
351 for (auto & entry : fMasterConns)
352 if (!connid || entry.connid == connid)
353 lst.emplace_back(entry);
354
355 return lst;
356}
357
358//////////////////////////////////////////////////////////////////////////////////////////
359/// Remove master connection - if any
360
362{
363 if (!fMaster) return;
364
365 bool isany = false;
366
367 {
368 std::lock_guard<std::mutex> grd(fConnMutex);
369
370 if (connid == 0) {
371 fMasterConns.clear();
372 } else {
373 for (auto iter = fMasterConns.begin(); iter != fMasterConns.end(); ++iter)
374 if (iter->connid == connid) {
375 fMasterConns.erase(iter);
376 break;
377 }
378 }
379
380 isany = fMasterConns.size() > 0;
381 }
382
383 if (!isany)
384 fMaster.reset();
385}
386
387//////////////////////////////////////////////////////////////////////////////////////////
388/// Process special http request, used to hold headless browser running
389/// Such requests should not be replied for the long time
390/// Be aware that function called directly from THttpServer thread, which is not same thread as window
391
392bool RWebWindow::ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg)
393{
394 std::string query = arg->GetQuery();
395
396 if (query.compare(0, 4, "key=") != 0)
397 return false;
398
399 std::string key = query.substr(4);
400
401 std::shared_ptr<THttpCallArg> prev;
402
403 bool found_key = false;
404
405 // use connection mutex to access hold request
406 {
407 std::lock_guard<std::mutex> grd(fConnMutex);
408 for (auto &entry : fPendingConn) {
409 if (entry->fKey == key) {
410 assert(!found_key); // indicate error if many same keys appears
411 found_key = true;
412 prev = std::move(entry->fHold);
413 entry->fHold = arg;
414 }
415 }
416
417 for (auto &conn : fConn) {
418 if (conn->fKey == key) {
419 assert(!found_key); // indicate error if many same keys appears
420 prev = std::move(conn->fHold);
421 conn->fHold = arg;
422 found_key = true;
423 }
424 }
425 }
426
427 if (prev) {
428 prev->SetTextContent("console.log('execute holder script'); if (window) window.close();");
429 prev->NotifyCondition();
430 }
431
432 return found_key;
433}
434
435//////////////////////////////////////////////////////////////////////////////////////////
436/// Provide data to user callback
437/// User callback must be executed in the window thread
438
439void RWebWindow::ProvideQueueEntry(unsigned connid, EQueueEntryKind kind, std::string &&arg)
440{
441 {
442 std::lock_guard<std::mutex> grd(fInputQueueMutex);
443 fInputQueue.emplace(connid, kind, std::move(arg));
444 }
445
446 // if special python mode is used, process events called from special thread
447 // there is no other way to get regular calls in main python thread,
448 // therefore invoke widgets callbacks directly - which potentially can be dangerous
450}
451
452//////////////////////////////////////////////////////////////////////////////////////////
453/// Invoke callbacks with existing data
454/// Must be called from appropriate thread
455
457{
458 if (fCallbacksThrdIdSet && (fCallbacksThrdId != std::this_thread::get_id()) && !force)
459 return;
460
461 while (true) {
462 unsigned connid;
463 EQueueEntryKind kind;
464 std::string arg;
465
466 {
467 std::lock_guard<std::mutex> grd(fInputQueueMutex);
468 if (fInputQueue.size() == 0)
469 return;
470 auto &entry = fInputQueue.front();
471 connid = entry.fConnId;
472 kind = entry.fKind;
473 arg = std::move(entry.fData);
474 fInputQueue.pop();
475 }
476
477 switch (kind) {
478 case kind_None: break;
479 case kind_Connect:
480 if (fConnCallback)
481 fConnCallback(connid);
482 break;
483 case kind_Data:
484 if (fDataCallback)
485 fDataCallback(connid, arg);
486 break;
487 case kind_Disconnect:
489 fDisconnCallback(connid);
490 break;
491 }
492 }
493}
494
495//////////////////////////////////////////////////////////////////////////////////////////
496/// Add display handle and associated key
497/// Key is random number generated when starting new window
498/// When client is connected, key should be supplied to correctly identify it
499
500unsigned RWebWindow::AddDisplayHandle(bool headless_mode, const std::string &key, std::unique_ptr<RWebDisplayHandle> &handle)
501{
502 std::lock_guard<std::mutex> grd(fConnMutex);
503
504 auto conn = std::make_shared<WebConn>(++fConnCnt, headless_mode, key);
505
506 std::swap(conn->fDisplayHandle, handle);
507
508 fPendingConn.emplace_back(conn);
509
510 return fConnCnt;
511}
512
513//////////////////////////////////////////////////////////////////////////////////////////
514/// Find connection with specified key.
515/// Must be used under connection mutex lock
516
517std::shared_ptr<RWebWindow::WebConn> RWebWindow::_FindConnWithKey(const std::string &key) const
518{
519 if (key.empty())
520 return nullptr;
521
522 for (auto &entry : fPendingConn) {
523 if (entry->fKey == key)
524 return entry;
525 }
526
527 for (auto &conn : fConn) {
528 if (conn->fKey == key)
529 return conn;
530 }
531
532 return nullptr;
533}
534
535//////////////////////////////////////////////////////////////////////////////////////////
536/// Returns true if provided key value already exists (in processes map or in existing connections)
537
538bool RWebWindow::HasKey(const std::string &key) const
539{
540 std::lock_guard<std::mutex> grd(fConnMutex);
541
542 auto conn = _FindConnWithKey(key);
543
544 return conn ? true : false;
545
546 return false;
547}
548
549//////////////////////////////////////////////////////////////////////////////////////////
550/// Generate new unique key for the window
551
552std::string RWebWindow::GenerateKey() const
553{
554 int ntry = 100000;
555 TRandom3 rnd;
556 rnd.SetSeed();
557 std::string key;
558
559 do {
560 key = std::to_string(rnd.Integer(0x100000));
561 } while ((--ntry > 0) && HasKey(key));
562
563 if (ntry <= 0) key.clear();
564
565 return key;
566}
567
568//////////////////////////////////////////////////////////////////////////////////////////
569/// Check if started process(es) establish connection. After timeout such processed will be killed
570/// Method invoked from http server thread, therefore appropriate mutex must be used on all relevant data
571
573{
574 if (!fMgr) return;
575
576 timestamp_t stamp = std::chrono::system_clock::now();
577
578 float tmout = fMgr->GetLaunchTmout();
579
580 ConnectionsList_t selected;
581
582 {
583 std::lock_guard<std::mutex> grd(fConnMutex);
584
585 auto pred = [&](std::shared_ptr<WebConn> &e) {
586 std::chrono::duration<double> diff = stamp - e->fSendStamp;
587
588 if (diff.count() > tmout) {
589 R__LOG_DEBUG(0, WebGUILog()) << "Halt process after " << diff.count() << " sec";
590 selected.emplace_back(e);
591 return true;
592 }
593
594 return false;
595 };
596
597 fPendingConn.erase(std::remove_if(fPendingConn.begin(), fPendingConn.end(), pred), fPendingConn.end());
598 }
599
600}
601
602
603//////////////////////////////////////////////////////////////////////////////////////////
604/// Check if there are connection which are inactive for longer time
605/// For instance, batch browser will be stopped if no activity for 30 sec is there
606
608{
609 timestamp_t stamp = std::chrono::system_clock::now();
610
611 double batch_tmout = 20.;
612
613 std::vector<std::shared_ptr<WebConn>> clr;
614
615 {
616 std::lock_guard<std::mutex> grd(fConnMutex);
617
618 auto pred = [&](std::shared_ptr<WebConn> &conn) {
619 std::chrono::duration<double> diff = stamp - conn->fSendStamp;
620 // introduce large timeout
621 if ((diff.count() > batch_tmout) && conn->fHeadlessMode) {
622 conn->fActive = false;
623 clr.emplace_back(conn);
624 return true;
625 }
626 return false;
627 };
628
629 fConn.erase(std::remove_if(fConn.begin(), fConn.end(), pred), fConn.end());
630 }
631
632 for (auto &entry : clr)
633 ProvideQueueEntry(entry->fConnId, kind_Disconnect, ""s);
634
635}
636
637/////////////////////////////////////////////////////////////////////////
638/// Configure maximal number of allowed connections - 0 is unlimited
639/// Will not affect already existing connections
640/// Default is 1 - the only client is allowed
641
642void RWebWindow::SetConnLimit(unsigned lmt)
643{
644 std::lock_guard<std::mutex> grd(fConnMutex);
645
646 fConnLimit = lmt;
647}
648
649/////////////////////////////////////////////////////////////////////////
650/// returns configured connections limit (0 - default)
651
653{
654 std::lock_guard<std::mutex> grd(fConnMutex);
655
656 return fConnLimit;
657}
658
659/////////////////////////////////////////////////////////////////////////
660/// Configures connection token (default none)
661/// When specified, in URL of webpage such token should be provided as &token=value parameter,
662/// otherwise web window will refuse connection
663
664void RWebWindow::SetConnToken(const std::string &token)
665{
666 std::lock_guard<std::mutex> grd(fConnMutex);
667
668 fConnToken = token;
669}
670
671/////////////////////////////////////////////////////////////////////////
672/// Returns configured connection token
673
674std::string RWebWindow::GetConnToken() const
675{
676 std::lock_guard<std::mutex> grd(fConnMutex);
677
678 return fConnToken;
679}
680
681//////////////////////////////////////////////////////////////////////////////////////////
682/// Processing of websockets call-backs, invoked from RWebWindowWSHandler
683/// Method invoked from http server thread, therefore appropriate mutex must be used on all relevant data
684
686{
687 if (arg.GetWSId() == 0)
688 return true;
689
690 if (arg.IsMethod("WS_CONNECT")) {
691
692 TUrl url;
693 url.SetOptions(arg.GetQuery());
694 bool check_key = RWebWindowWSHandler::GetBoolEnv("WebGui.OnetimeKey") == 1;
695
696 std::lock_guard<std::mutex> grd(fConnMutex);
697
698 // refuse connection when number of connections exceed limit
699 if (fConnLimit && (fConn.size() >= fConnLimit))
700 return false;
701
702 if (!fConnToken.empty()) {
703 // refuse connection which does not provide proper token
704 if (!url.HasOption("token") || (fConnToken != url.GetValueFromOptions("token"))) {
705 R__LOG_DEBUG(0, WebGUILog()) << "Refuse connection without proper token";
706 return false;
707 }
708 }
709
710 if (check_key) {
711 if(!url.HasOption("key")) {
712 R__LOG_DEBUG(0, WebGUILog()) << "key parameter not provided in url";
713 return false;
714 }
715
716 auto key = url.GetValueFromOptions("key");
717
718 auto conn = _FindConnWithKey(key);
719 if (!conn) {
720 R__LOG_ERROR(WebGUILog()) << "connection with key " << key << " not found ";
721 return false;
722 }
723
724 if (conn->fKeyUsed > 0) {
725 R__LOG_ERROR(WebGUILog()) << "key " << key << " was used for establishing connection, call ShowWindow again";
726 return false;
727 }
728
729 conn->fKeyUsed = 1;
730 }
731
732 return true;
733 }
734
735 if (arg.IsMethod("WS_READY")) {
736 auto conn = FindOrCreateConnection(arg.GetWSId(), true, arg.GetQuery());
737
738 if (conn) {
739 R__LOG_ERROR(WebGUILog()) << "WSHandle with given websocket id " << arg.GetWSId() << " already exists";
740 return false;
741 }
742
743 return true;
744 }
745
746 if (arg.IsMethod("WS_CLOSE")) {
747 // connection is closed, one can remove handle, associated window will be closed
748
749 auto conn = RemoveConnection(arg.GetWSId());
750
751 if (conn) {
752 ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
753 bool do_clear_on_close = false;
754 if (conn->fKeyUsed < 0) {
755 // case when same handle want to be reused by client with new key
756 std::lock_guard<std::mutex> grd(fConnMutex);
757 conn->fKeyUsed = 0;
758 conn->fConnId = ++fConnCnt; // change connection id to avoid confusion
759 conn->ResetData();
760 fPendingConn.emplace_back(conn);
761 } else {
762 std::lock_guard<std::mutex> grd(fConnMutex);
763 do_clear_on_close = (fPendingConn.size() == 0) && (fConn.size() == 0);
764 }
765
766 if (do_clear_on_close)
767 fClearOnClose.reset();
768 }
769
770 return true;
771 }
772
773 if (!arg.IsMethod("WS_DATA")) {
774 R__LOG_ERROR(WebGUILog()) << "only WS_DATA request expected!";
775 return false;
776 }
777
778 auto conn = FindConnection(arg.GetWSId());
779
780 if (!conn) {
781 R__LOG_ERROR(WebGUILog()) << "Get websocket data without valid connection - ignore!!!";
782 return false;
783 }
784
785 if (arg.GetPostDataLength() <= 0)
786 return true;
787
788 // here processing of received data should be performed
789 // this is task for the implemented windows
790
791 const char *buf = (const char *)arg.GetPostData();
792 char *str_end = nullptr;
793
794 unsigned long ackn_oper = std::strtoul(buf, &str_end, 10);
795 if (!str_end || *str_end != ':') {
796 R__LOG_ERROR(WebGUILog()) << "missing number of acknowledged operations";
797 return false;
798 }
799
800 unsigned long can_send = std::strtoul(str_end + 1, &str_end, 10);
801 if (!str_end || *str_end != ':') {
802 R__LOG_ERROR(WebGUILog()) << "missing can_send counter";
803 return false;
804 }
805
806 unsigned long nchannel = std::strtoul(str_end + 1, &str_end, 10);
807 if (!str_end || *str_end != ':') {
808 R__LOG_ERROR(WebGUILog()) << "missing channel number";
809 return false;
810 }
811
812 Long_t processed_len = (str_end + 1 - buf);
813
814 if (processed_len > arg.GetPostDataLength()) {
815 R__LOG_ERROR(WebGUILog()) << "corrupted buffer";
816 return false;
817 }
818
819 std::string cdata(str_end + 1, arg.GetPostDataLength() - processed_len);
820
821 timestamp_t stamp = std::chrono::system_clock::now();
822
823 {
824 std::lock_guard<std::mutex> grd(conn->fMutex);
825
826 conn->fSendCredits += ackn_oper;
827 conn->fRecvCount++;
828 conn->fClientCredits = (int)can_send;
829 conn->fRecvStamp = stamp;
830 }
831
832 if (fProtocolCnt >= 0)
833 if (!fProtocolConnId || (conn->fConnId == fProtocolConnId)) {
834 fProtocolConnId = conn->fConnId; // remember connection
835
836 // record send event only for normal channel or very first message via ch0
837 if ((nchannel != 0) || (cdata.find("READY=") == 0)) {
838 if (fProtocol.length() > 2)
839 fProtocol.insert(fProtocol.length() - 1, ",");
840 fProtocol.insert(fProtocol.length() - 1, "\"send\"");
841
842 std::ofstream pfs(fProtocolFileName);
843 pfs.write(fProtocol.c_str(), fProtocol.length());
844 pfs.close();
845 }
846 }
847
848 if (nchannel == 0) {
849 // special system channel
850 if ((cdata.compare(0, 6, "READY=") == 0) && !conn->fReady) {
851
852 std::string key = cdata.substr(6);
853
854 if (key.empty() && IsNativeOnlyConn()) {
855 RemoveConnection(conn->fWSId);
856 return false;
857 }
858
859 if (!key.empty() && !conn->fKey.empty() && (conn->fKey != key)) {
860 R__LOG_ERROR(WebGUILog()) << "Key mismatch after established connection " << key << " != " << conn->fKey;
861 RemoveConnection(conn->fWSId);
862 return false;
863 }
864
865 if (!fPanelName.empty()) {
866 // initialization not yet finished, appropriate panel should be started
867 Send(conn->fConnId, "SHOWPANEL:"s + fPanelName);
868 conn->fReady = 5;
869 } else {
870 ProvideQueueEntry(conn->fConnId, kind_Connect, ""s);
871 conn->fReady = 10;
872 }
873 } else if (cdata.compare(0, 8, "CLOSECH=") == 0) {
874 int channel = std::stoi(cdata.substr(8));
875 auto iter = conn->fEmbed.find(channel);
876 if (iter != conn->fEmbed.end()) {
877 iter->second->ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
878 conn->fEmbed.erase(iter);
879 }
880 } else if (cdata.compare(0, 7, "RESIZE=") == 0) {
881 auto p = cdata.find(",");
882 if (p != std::string::npos) {
883 auto width = std::stoi(cdata.substr(7, p - 7));
884 auto height = std::stoi(cdata.substr(p + 1));
885 if ((width > 0) && (height > 0) && conn->fDisplayHandle)
886 conn->fDisplayHandle->Resize(width, height);
887 }
888 } else if (cdata == "GENERATE_KEY") {
889
890 if (fMaster) {
891 R__LOG_ERROR(WebGUILog()) << "Not able to generate new key with master connections";
892 } else {
893 auto newkey = GenerateKey();
894 if(newkey.empty()) {
895 R__LOG_ERROR(WebGUILog()) << "Fail to generate new key by GENERATE_KEY request";
896 } else {
897 SubmitData(conn->fConnId, true, "NEW_KEY="s + newkey, -1);
898 conn->fKey = newkey;
899 conn->fKeyUsed = -1;
900 }
901 }
902 }
903 } else if (fPanelName.length() && (conn->fReady < 10)) {
904 if (cdata == "PANEL_READY") {
905 R__LOG_DEBUG(0, WebGUILog()) << "Get panel ready " << fPanelName;
906 ProvideQueueEntry(conn->fConnId, kind_Connect, ""s);
907 conn->fReady = 10;
908 } else {
909 ProvideQueueEntry(conn->fConnId, kind_Disconnect, ""s);
910 RemoveConnection(conn->fWSId);
911 }
912 } else if (nchannel == 1) {
913 ProvideQueueEntry(conn->fConnId, kind_Data, std::move(cdata));
914 } else if (nchannel > 1) {
915 // process embed window
916 auto embed_window = conn->fEmbed[nchannel];
917 if (embed_window)
918 embed_window->ProvideQueueEntry(conn->fConnId, kind_Data, std::move(cdata));
919 }
920
922
923 return true;
924}
925
926//////////////////////////////////////////////////////////////////////////////////////////
927/// Complete websocket send operation
928/// Clear "doing send" flag and check if next operation has to be started
929
930void RWebWindow::CompleteWSSend(unsigned wsid)
931{
932 auto conn = FindConnection(wsid);
933
934 if (!conn)
935 return;
936
937 {
938 std::lock_guard<std::mutex> grd(conn->fMutex);
939 conn->fDoingSend = false;
940 }
941
942 CheckDataToSend(conn);
943}
944
945//////////////////////////////////////////////////////////////////////////////////////////
946/// Internal method to prepare text part of send data
947/// Should be called under locked connection mutex
948
949std::string RWebWindow::_MakeSendHeader(std::shared_ptr<WebConn> &conn, bool txt, const std::string &data, int chid)
950{
951 std::string buf;
952
953 if (!conn->fWSId || !fWSHandler) {
954 R__LOG_ERROR(WebGUILog()) << "try to send text data when connection not established";
955 return buf;
956 }
957
958 if (conn->fSendCredits <= 0) {
959 R__LOG_ERROR(WebGUILog()) << "No credits to send text data via connection";
960 return buf;
961 }
962
963 if (conn->fDoingSend) {
964 R__LOG_ERROR(WebGUILog()) << "Previous send operation not completed yet";
965 return buf;
966 }
967
968 if (txt)
969 buf.reserve(data.length() + 100);
970
971 buf.append(std::to_string(conn->fRecvCount));
972 buf.append(":");
973 buf.append(std::to_string(conn->fSendCredits));
974 buf.append(":");
975 conn->fRecvCount = 0; // we confirm how many packages was received
976 conn->fSendCredits--;
977
978 buf.append(std::to_string(chid));
979 buf.append(":");
980
981 if (txt) {
982 buf.append(data);
983 } else if (data.length()==0) {
984 buf.append("$$nullbinary$$");
985 } else {
986 buf.append("$$binary$$");
987 }
988
989 return buf;
990}
991
992//////////////////////////////////////////////////////////////////////////////////////////
993/// Checks if one should send data for specified connection
994/// Returns true when send operation was performed
995
996bool RWebWindow::CheckDataToSend(std::shared_ptr<WebConn> &conn)
997{
998 std::string hdr, data;
999
1000 {
1001 std::lock_guard<std::mutex> grd(conn->fMutex);
1002
1003 if (!conn->fActive || (conn->fSendCredits <= 0) || conn->fDoingSend) return false;
1004
1005 if (!conn->fQueue.empty()) {
1006 QueueItem &item = conn->fQueue.front();
1007 hdr = _MakeSendHeader(conn, item.fText, item.fData, item.fChID);
1008 if (!hdr.empty() && !item.fText)
1009 data = std::move(item.fData);
1010 conn->fQueue.pop();
1011 } else if ((conn->fClientCredits < 3) && (conn->fRecvCount > 1)) {
1012 // give more credits to the client
1013 hdr = _MakeSendHeader(conn, true, "KEEPALIVE", 0);
1014 }
1015
1016 if (hdr.empty()) return false;
1017
1018 conn->fDoingSend = true;
1019 }
1020
1021 int res = 0;
1022
1023 if (data.empty()) {
1024 res = fWSHandler->SendCharStarWS(conn->fWSId, hdr.c_str());
1025 } else {
1026 res = fWSHandler->SendHeaderWS(conn->fWSId, hdr.c_str(), data.data(), data.length());
1027 }
1028
1029 // submit operation, will be processed
1030 if (res >=0) return true;
1031
1032 // failure, clear sending flag
1033 std::lock_guard<std::mutex> grd(conn->fMutex);
1034 conn->fDoingSend = false;
1035 return false;
1036}
1037
1038
1039//////////////////////////////////////////////////////////////////////////////////////////
1040/// Checks if new data can be send (internal use only)
1041/// If necessary, provide credits to the client
1042/// \param only_once if true, data sending performed once or until there is no data to send
1043
1045{
1046 // make copy of all connections to be independent later, only active connections are checked
1047 auto arr = GetWindowConnections(0, true);
1048
1049 do {
1050 bool isany = false;
1051
1052 for (auto &conn : arr)
1053 if (CheckDataToSend(conn))
1054 isany = true;
1055
1056 if (!isany) break;
1057
1058 } while (!only_once);
1059}
1060
1061///////////////////////////////////////////////////////////////////////////////////
1062/// Special method to process all internal activity when window runs in separate thread
1063
1065{
1067
1069
1071
1073}
1074
1075///////////////////////////////////////////////////////////////////////////////////
1076/// Returns window address which is used in URL
1077
1078std::string RWebWindow::GetAddr() const
1079{
1080 return fWSHandler->GetName();
1081}
1082
1083///////////////////////////////////////////////////////////////////////////////////
1084/// Returns relative URL address for the specified window
1085/// Address can be required if one needs to access data from one window into another window
1086/// Used for instance when inserting panel into canvas
1087
1088std::string RWebWindow::GetRelativeAddr(const std::shared_ptr<RWebWindow> &win) const
1089{
1090 return GetRelativeAddr(*win);
1091}
1092
1093///////////////////////////////////////////////////////////////////////////////////
1094/// Returns relative URL address for the specified window
1095/// Address can be required if one needs to access data from one window into another window
1096/// Used for instance when inserting panel into canvas
1097
1099{
1100 if (fMgr != win.fMgr) {
1101 R__LOG_ERROR(WebGUILog()) << "Same web window manager should be used";
1102 return "";
1103 }
1104
1105 std::string res("../");
1106 res.append(win.GetAddr());
1107 res.append("/");
1108 return res;
1109}
1110
1111/////////////////////////////////////////////////////////////////////////
1112/// Set client version, used as prefix in scripts URL
1113/// When changed, web browser will reload all related JS files while full URL will be different
1114/// Default is empty value - no extra string in URL
1115/// Version should be string like "1.2" or "ver1.subv2" and not contain any special symbols
1116
1117void RWebWindow::SetClientVersion(const std::string &vers)
1118{
1119 std::lock_guard<std::mutex> grd(fConnMutex);
1120 fClientVersion = vers;
1121}
1122
1123/////////////////////////////////////////////////////////////////////////
1124/// Returns current client version
1125
1127{
1128 std::lock_guard<std::mutex> grd(fConnMutex);
1129 return fClientVersion;
1130}
1131
1132/////////////////////////////////////////////////////////////////////////
1133/// Set arbitrary JSON data, which is accessible via conn.getUserArgs() method in JavaScript
1134/// This JSON code injected into main HTML document into connectWebWindow({})
1135/// Must be set before RWebWindow::Show() method is called
1136/// \param args - arbitrary JSON data which can be provided to client side
1137
1138void RWebWindow::SetUserArgs(const std::string &args)
1139{
1140 std::lock_guard<std::mutex> grd(fConnMutex);
1141 fUserArgs = args;
1142}
1143
1144/////////////////////////////////////////////////////////////////////////
1145/// Returns configured user arguments for web window
1146/// See \ref SetUserArgs method for more details
1147
1148std::string RWebWindow::GetUserArgs() const
1149{
1150 std::lock_guard<std::mutex> grd(fConnMutex);
1151 return fUserArgs;
1152}
1153
1154///////////////////////////////////////////////////////////////////////////////////
1155/// Returns current number of active clients connections
1156/// \param with_pending if true, also pending (not yet established) connection accounted
1157
1158int RWebWindow::NumConnections(bool with_pending) const
1159{
1160 bool is_master = !!fMaster;
1161
1162 std::lock_guard<std::mutex> grd(fConnMutex);
1163
1164 if (is_master)
1165 return fMasterConns.size();
1166
1167 auto sz = fConn.size();
1168 if (with_pending)
1169 sz += fPendingConn.size();
1170 return sz;
1171}
1172
1173///////////////////////////////////////////////////////////////////////////////////
1174/// Configures recording of communication data in protocol file
1175/// Provided filename will be used to store JSON array with names of written files - text or binary
1176/// If data was send from client, "send" entry will be placed. JSON file will look like:
1177///
1178/// ["send", "msg0.txt", "send", "msg1.txt", "msg2.txt"]
1179///
1180/// If empty file name is provided, data recording will be disabled
1181/// Recorded data can be used in JSROOT directly to test client code without running C++ server
1182
1183void RWebWindow::RecordData(const std::string &fname, const std::string &fprefix)
1184{
1185 fProtocolFileName = fname;
1186 fProtocolCnt = fProtocolFileName.empty() ? -1 : 0;
1188 fProtocolPrefix = fprefix;
1189 fProtocol = "[]"; // empty array
1190}
1191
1192///////////////////////////////////////////////////////////////////////////////////
1193/// Returns connection id for specified connection sequence number
1194/// Only active connections are returned - where clients confirms connection
1195/// Total number of connections can be retrieved with NumConnections() method
1196/// \param num connection sequence number
1197
1198unsigned RWebWindow::GetConnectionId(int num) const
1199{
1200 bool is_master = !!fMaster;
1201
1202 std::lock_guard<std::mutex> grd(fConnMutex);
1203
1204 if (is_master)
1205 return (num >= 0) && (num < (int)fMasterConns.size()) ? fMasterConns[num].connid : 0;
1206
1207 return ((num >= 0) && (num < (int)fConn.size()) && fConn[num]->fActive) ? fConn[num]->fConnId : 0;
1208}
1209
1210///////////////////////////////////////////////////////////////////////////////////
1211/// returns vector with all existing connections ids
1212/// One also can exclude specified connection from return result,
1213/// which can be useful to be able reply too all but this connections
1214
1215std::vector<unsigned> RWebWindow::GetConnections(unsigned excludeid) const
1216{
1217 std::vector<unsigned> res;
1218
1219 bool is_master = !!fMaster;
1220
1221 std::lock_guard<std::mutex> grd(fConnMutex);
1222
1223 if (is_master) {
1224 for (auto & entry : fMasterConns)
1225 if (entry.connid != excludeid)
1226 res.emplace_back(entry.connid);
1227 } else {
1228 for (auto & entry : fConn)
1229 if (entry->fActive && (entry->fConnId != excludeid))
1230 res.emplace_back(entry->fConnId);
1231 }
1232
1233 return res;
1234}
1235
1236///////////////////////////////////////////////////////////////////////////////////
1237/// returns true if specified connection id exists
1238/// \param connid connection id (0 - any)
1239/// \param only_active when true only active connection will be checked, otherwise also pending (not yet established) connections are checked
1240
1241bool RWebWindow::HasConnection(unsigned connid, bool only_active) const
1242{
1243 std::lock_guard<std::mutex> grd(fConnMutex);
1244
1245 for (auto &conn : fConn) {
1246 if (connid && (conn->fConnId != connid))
1247 continue;
1248 if (conn->fActive || !only_active)
1249 return true;
1250 }
1251
1252 if (!only_active)
1253 for (auto &conn : fPendingConn) {
1254 if (!connid || (conn->fConnId == connid))
1255 return true;
1256 }
1257
1258 return false;
1259}
1260
1261///////////////////////////////////////////////////////////////////////////////////
1262/// Closes all connection to clients
1263/// Normally leads to closing of all correspondent browser windows
1264/// Some browsers (like firefox) do not allow by default to close window
1265
1267{
1268 SubmitData(0, true, "CLOSE", 0);
1269}
1270
1271///////////////////////////////////////////////////////////////////////////////////
1272/// Close specified connection
1273/// \param connid connection id, when 0 - all connections will be closed
1274
1275void RWebWindow::CloseConnection(unsigned connid)
1276{
1277 if (connid)
1278 SubmitData(connid, true, "CLOSE", 0);
1279}
1280
1281///////////////////////////////////////////////////////////////////////////////////
1282/// returns connection list (or all active connections)
1283/// \param connid connection id, when 0 - all existing connections are returned
1284/// \param only_active when true, only active (already established) connections are returned
1285
1287{
1289
1290 {
1291 std::lock_guard<std::mutex> grd(fConnMutex);
1292
1293 for (auto &conn : fConn) {
1294 if ((conn->fActive || !only_active) && (!connid || (conn->fConnId == connid)))
1295 arr.push_back(conn);
1296 }
1297
1298 if (!only_active)
1299 for (auto &conn : fPendingConn)
1300 if (!connid || (conn->fConnId == connid))
1301 arr.push_back(conn);
1302 }
1303
1304 return arr;
1305}
1306
1307///////////////////////////////////////////////////////////////////////////////////
1308/// Returns true if sending via specified connection can be performed
1309/// \param connid connection id, when 0 - all existing connections are checked
1310/// \param direct when true, checks if direct sending (without queuing) is possible
1311
1312bool RWebWindow::CanSend(unsigned connid, bool direct) const
1313{
1314 auto arr = GetWindowConnections(connid, direct); // for direct sending connection has to be active
1315
1316 auto maxqlen = GetMaxQueueLength();
1317
1318 for (auto &conn : arr) {
1319
1320 std::lock_guard<std::mutex> grd(conn->fMutex);
1321
1322 if (direct && (!conn->fQueue.empty() || (conn->fSendCredits == 0) || conn->fDoingSend))
1323 return false;
1324
1325 if (conn->fQueue.size() >= maxqlen)
1326 return false;
1327 }
1328
1329 return true;
1330}
1331
1332///////////////////////////////////////////////////////////////////////////////////
1333/// Returns send queue length for specified connection
1334/// \param connid connection id, 0 - maximal value for all connections is returned
1335/// If wrong connection id specified, -1 is return
1336
1337int RWebWindow::GetSendQueueLength(unsigned connid) const
1338{
1339 int maxq = -1;
1340
1341 for (auto &conn : GetWindowConnections(connid)) {
1342 std::lock_guard<std::mutex> grd(conn->fMutex);
1343 int len = conn->fQueue.size();
1344 if (len > maxq) maxq = len;
1345 }
1346
1347 return maxq;
1348}
1349
1350///////////////////////////////////////////////////////////////////////////////////
1351/// Internal method to send data
1352/// \param connid connection id, when 0 - data will be send to all connections
1353/// \param txt is text message that should be sent
1354/// \param data data to be std-moved to SubmitData function
1355/// \param chid channel id, 1 - normal communication, 0 - internal with highest priority
1356
1357void RWebWindow::SubmitData(unsigned connid, bool txt, std::string &&data, int chid)
1358{
1359 if (fMaster) {
1360 auto lst = GetMasterConnections(connid);
1361 auto cnt = lst.size();
1362 for (auto & entry : lst)
1363 if (--cnt)
1364 fMaster->SubmitData(entry.connid, txt, std::string(data), entry.channel);
1365 else
1366 fMaster->SubmitData(entry.connid, txt, std::move(data), entry.channel);
1367 return;
1368 }
1369
1370 auto arr = GetWindowConnections(connid);
1371 auto cnt = arr.size();
1372 auto maxqlen = GetMaxQueueLength();
1373
1374 bool clear_queue = false;
1375
1376 if (chid == -1) {
1377 chid = 0;
1378 clear_queue = true;
1379 }
1380
1381 timestamp_t stamp = std::chrono::system_clock::now();
1382
1383 for (auto &conn : arr) {
1384
1385 if (fProtocolCnt >= 0)
1386 if (!fProtocolConnId || (conn->fConnId == fProtocolConnId)) {
1387 fProtocolConnId = conn->fConnId; // remember connection
1388 std::string fname = fProtocolPrefix;
1389 fname.append("msg");
1390 fname.append(std::to_string(fProtocolCnt++));
1391 if (chid > 1) {
1392 fname.append("_ch");
1393 fname.append(std::to_string(chid));
1394 }
1395 fname.append(txt ? ".txt" : ".bin");
1396
1397 std::ofstream ofs(fname);
1398 ofs.write(data.c_str(), data.length());
1399 ofs.close();
1400
1401 if (fProtocol.length() > 2)
1402 fProtocol.insert(fProtocol.length() - 1, ",");
1403 fProtocol.insert(fProtocol.length() - 1, "\""s + fname + "\""s);
1404
1405 std::ofstream pfs(fProtocolFileName);
1406 pfs.write(fProtocol.c_str(), fProtocol.length());
1407 pfs.close();
1408 }
1409
1410 conn->fSendStamp = stamp;
1411
1412 std::lock_guard<std::mutex> grd(conn->fMutex);
1413
1414 if (clear_queue) {
1415 while (!conn->fQueue.empty())
1416 conn->fQueue.pop();
1417 }
1418
1419 if (conn->fQueue.size() < maxqlen) {
1420 if (--cnt)
1421 conn->fQueue.emplace(chid, txt, std::string(data)); // make copy
1422 else
1423 conn->fQueue.emplace(chid, txt, std::move(data)); // move content
1424 } else {
1425 R__LOG_ERROR(WebGUILog()) << "Maximum queue length achieved";
1426 }
1427 }
1428
1430}
1431
1432///////////////////////////////////////////////////////////////////////////////////
1433/// Sends data to specified connection
1434/// \param connid connection id, when 0 - data will be send to all connections
1435/// \param data data to be copied to SubmitData function
1436
1437void RWebWindow::Send(unsigned connid, const std::string &data)
1438{
1439 SubmitData(connid, true, std::string(data), 1);
1440}
1441
1442///////////////////////////////////////////////////////////////////////////////////
1443/// Send binary data to specified connection
1444/// \param connid connection id, when 0 - data will be send to all connections
1445/// \param data data to be std-moved to SubmitData function
1446
1447void RWebWindow::SendBinary(unsigned connid, std::string &&data)
1448{
1449 SubmitData(connid, false, std::move(data), 1);
1450}
1451
1452///////////////////////////////////////////////////////////////////////////////////
1453/// Send binary data to specified connection
1454/// \param connid connection id, when 0 - data will be send to all connections
1455/// \param data pointer to binary data
1456/// \param len number of bytes in data
1457
1458void RWebWindow::SendBinary(unsigned connid, const void *data, std::size_t len)
1459{
1460 std::string buf;
1461 buf.resize(len);
1462 std::copy((const char *)data, (const char *)data + len, buf.begin());
1463 SubmitData(connid, false, std::move(buf), 1);
1464}
1465
1466///////////////////////////////////////////////////////////////////////////////////
1467/// Assign thread id which has to be used for callbacks
1468/// WARNING!!! only for expert use
1469/// Automatically done at the moment when any callback function is invoked
1470/// Can be invoked once again if window Run method will be invoked from other thread
1471/// Normally should be invoked before Show() method is called
1472
1474{
1475 fUseServerThreads = false;
1476 fUseProcessEvents = false;
1477 fProcessMT = false;
1478 fCallbacksThrdIdSet = true;
1479 fCallbacksThrdId = std::this_thread::get_id();
1481 fProcessMT = true;
1482 } else if (fMgr->IsUseHttpThread()) {
1483 // special thread is used by the manager, but main thread used for the canvas - not supported
1484 R__LOG_ERROR(WebGUILog()) << "create web window from main thread when THttpServer created with special thread - not supported";
1485 }
1486}
1487
1488/////////////////////////////////////////////////////////////////////////////////
1489/// Let use THttpServer threads to process requests
1490/// WARNING!!! only for expert use
1491/// Should be only used when application provides proper locking and
1492/// does not block. Such mode provides minimal possible latency
1493/// Must be called before callbacks are assigned
1494
1496{
1497 fUseServerThreads = true;
1498 fUseProcessEvents = false;
1499 fCallbacksThrdIdSet = false;
1500 fProcessMT = true;
1501}
1502
1503/////////////////////////////////////////////////////////////////////////////////
1504/// Start special thread which will be used by the window to handle all callbacks
1505/// One has to be sure, that access to global ROOT structures are minimized and
1506/// protected with ROOT::EnableThreadSafety(); call
1507
1509{
1510 if (fHasWindowThrd) {
1511 R__LOG_WARNING(WebGUILog()) << "thread already started for the window";
1512 return;
1513 }
1514
1515 fHasWindowThrd = true;
1516
1517 std::thread thrd([this] {
1519 while(fHasWindowThrd)
1520 Run(0.1);
1521 fCallbacksThrdIdSet = false;
1522 });
1523
1524 fWindowThrd = std::move(thrd);
1525}
1526
1527/////////////////////////////////////////////////////////////////////////////////
1528/// Stop special thread
1529
1531{
1532 if (!fHasWindowThrd)
1533 return;
1534
1535 fHasWindowThrd = false;
1536 fWindowThrd.join();
1537}
1538
1539
1540/////////////////////////////////////////////////////////////////////////////////
1541/// Set call-back function for data, received from the clients via websocket
1542///
1543/// Function should have signature like void func(unsigned connid, const std::string &data)
1544/// First argument identifies connection (unique for each window), second argument is received data
1545///
1546/// At the moment when callback is assigned, RWebWindow working thread is detected.
1547/// If called not from main application thread, RWebWindow::Run() function must be regularly called from that thread.
1548///
1549/// Most simple way to assign call-back - use of c++11 lambdas like:
1550/// ~~~ {.cpp}
1551/// auto win = RWebWindow::Create();
1552/// win->SetDefaultPage("file:./page.htm");
1553/// win->SetDataCallBack(
1554/// [](unsigned connid, const std::string &data) {
1555/// printf("Conn:%u data:%s\n", connid, data.c_str());
1556/// }
1557/// );
1558/// win->Show();
1559/// ~~~
1560
1562{
1565 fDataCallback = func;
1566}
1567
1568/////////////////////////////////////////////////////////////////////////////////
1569/// Set call-back function for new connection
1570
1572{
1575 fConnCallback = func;
1576}
1577
1578/////////////////////////////////////////////////////////////////////////////////
1579/// Set call-back function for disconnecting
1580
1582{
1585 fDisconnCallback = func;
1586}
1587
1588/////////////////////////////////////////////////////////////////////////////////
1589/// Set handle which is cleared when last active connection is closed
1590/// Typically can be used to destroy web-based widget at such moment
1591
1592void RWebWindow::SetClearOnClose(const std::shared_ptr<void> &handle)
1593{
1594 fClearOnClose = handle;
1595}
1596
1597/////////////////////////////////////////////////////////////////////////////////
1598/// Set call-backs function for connect, data and disconnect events
1599
1601{
1604 fConnCallback = conn;
1606 fDisconnCallback = disconn;
1607}
1608
1609/////////////////////////////////////////////////////////////////////////////////
1610/// Waits until provided check function or lambdas returns non-zero value
1611/// Check function has following signature: int func(double spent_tm)
1612/// Waiting will be continued, if function returns zero.
1613/// Parameter spent_tm is time in seconds, which already spent inside the function
1614/// First non-zero value breaks loop and result is returned.
1615/// Runs application mainloop and short sleeps in-between
1616
1618{
1619 return fMgr->WaitFor(*this, check);
1620}
1621
1622/////////////////////////////////////////////////////////////////////////////////
1623/// Waits until provided check function or lambdas returns non-zero value
1624/// Check function has following signature: int func(double spent_tm)
1625/// Waiting will be continued, if function returns zero.
1626/// Parameter spent_tm in lambda is time in seconds, which already spent inside the function
1627/// First non-zero value breaks waiting loop and result is returned (or 0 if time is expired).
1628/// Runs application mainloop and short sleeps in-between
1629/// WebGui.OperationTmout rootrc parameter defines waiting time in seconds
1630
1632{
1633 return fMgr->WaitFor(*this, check, true, GetOperationTmout());
1634}
1635
1636/////////////////////////////////////////////////////////////////////////////////
1637/// Waits until provided check function or lambdas returns non-zero value
1638/// Check function has following signature: int func(double spent_tm)
1639/// Waiting will be continued, if function returns zero.
1640/// Parameter spent_tm in lambda is time in seconds, which already spent inside the function
1641/// First non-zero value breaks waiting loop and result is returned (or 0 if time is expired).
1642/// Runs application mainloop and short sleeps in-between
1643/// duration (in seconds) defines waiting time
1644
1646{
1647 return fMgr->WaitFor(*this, check, true, duration);
1648}
1649
1650
1651/////////////////////////////////////////////////////////////////////////////////
1652/// Run window functionality for specified time
1653/// If no action can be performed - just sleep specified time
1654
1655void RWebWindow::Run(double tm)
1656{
1657 if (!fCallbacksThrdIdSet || (fCallbacksThrdId != std::this_thread::get_id())) {
1658 R__LOG_WARNING(WebGUILog()) << "Change thread id where RWebWindow is executed";
1659 fCallbacksThrdIdSet = true;
1660 fCallbacksThrdId = std::this_thread::get_id();
1661 }
1662
1663 if (tm <= 0) {
1664 Sync();
1665 } else {
1666 WaitForTimed([](double) { return 0; }, tm);
1667 }
1668}
1669
1670
1671/////////////////////////////////////////////////////////////////////////////////
1672/// Add embed window
1673
1674unsigned RWebWindow::AddEmbedWindow(std::shared_ptr<RWebWindow> window, unsigned connid, int channel)
1675{
1676 if (channel < 2)
1677 return 0;
1678
1679 auto arr = GetWindowConnections(connid, true);
1680 if (arr.size() == 0)
1681 return 0;
1682
1683 // check if channel already occupied
1684 if (arr[0]->fEmbed.find(channel) != arr[0]->fEmbed.end())
1685 return 0;
1686
1687 arr[0]->fEmbed[channel] = window;
1688
1689 return arr[0]->fConnId;
1690}
1691
1692/////////////////////////////////////////////////////////////////////////////////
1693/// Remove RWebWindow associated with the channelfEmbed
1694
1695void RWebWindow::RemoveEmbedWindow(unsigned connid, int channel)
1696{
1697 auto arr = GetWindowConnections(connid);
1698
1699 for (auto &conn : arr) {
1700 auto iter = conn->fEmbed.find(channel);
1701 if (iter != conn->fEmbed.end())
1702 conn->fEmbed.erase(iter);
1703 }
1704}
1705
1706
1707/////////////////////////////////////////////////////////////////////////////////
1708/// Create new RWebWindow
1709/// Using default RWebWindowsManager
1710
1711std::shared_ptr<RWebWindow> RWebWindow::Create()
1712{
1713 return RWebWindowsManager::Instance()->CreateWindow();
1714}
1715
1716/////////////////////////////////////////////////////////////////////////////////
1717/// Terminate ROOT session
1718/// Tries to correctly close THttpServer, associated with RWebWindowsManager
1719/// After that exit from process
1720
1722{
1723
1724 // workaround to release all connection-specific handles as soon as possible
1725 // required to work with QWebEngine
1726 // once problem solved, can be removed here
1727 ConnectionsList_t arr1, arr2;
1728
1729 {
1730 std::lock_guard<std::mutex> grd(fConnMutex);
1731 std::swap(arr1, fConn);
1732 std::swap(arr2, fPendingConn);
1733 }
1734
1735 fMgr->Terminate();
1736}
1737
1738/////////////////////////////////////////////////////////////////////////////////
1739/// Static method to show web window
1740/// Has to be used instead of RWebWindow::Show() when window potentially can be embed into other windows
1741/// Soon RWebWindow::Show() method will be done protected
1742
1743unsigned RWebWindow::ShowWindow(std::shared_ptr<RWebWindow> window, const RWebDisplayArgs &args)
1744{
1745 if (!window)
1746 return 0;
1747
1749 if (args.fMaster && window->fMaster && window->fMaster != args.fMaster) {
1750 R__LOG_ERROR(WebGUILog()) << "Cannot use different master for same RWebWindow";
1751 return 0;
1752 }
1753
1754 unsigned connid = args.fMaster ? args.fMaster->AddEmbedWindow(window, args.fMasterConnection, args.fMasterChannel) : 0;
1755
1756 if (connid > 0) {
1757
1758 window->RemoveMasterConnection(connid);
1759
1760 window->AddMasterConnection(args.fMaster, connid, args.fMasterChannel);
1761
1762 // inform client that connection is established and window initialized
1763 args.fMaster->SubmitData(connid, true, "EMBED_DONE"s, args.fMasterChannel);
1764
1765 // provide call back for window itself that connection is ready
1766 window->ProvideQueueEntry(connid, kind_Connect, ""s);
1767 }
1768
1769 return connid;
1770 }
1771
1772 return window->Show(args);
1773}
1774
1775std::function<bool(const std::shared_ptr<RWebWindow> &, unsigned, const std::string &)> RWebWindow::gStartDialogFunc = nullptr;
1776
1777/////////////////////////////////////////////////////////////////////////////////////
1778/// Configure func which has to be used for starting dialog
1779
1780
1781void RWebWindow::SetStartDialogFunc(std::function<bool(const std::shared_ptr<RWebWindow> &, unsigned, const std::string &)> func)
1782{
1783 gStartDialogFunc = func;
1784}
1785
1786/////////////////////////////////////////////////////////////////////////////////////
1787/// Check if this could be the message send by client to start new file dialog
1788/// If returns true, one can call RWebWindow::EmbedFileDialog() to really create file dialog
1789/// instance inside existing widget
1790
1791bool RWebWindow::IsFileDialogMessage(const std::string &msg)
1792{
1793 return msg.compare(0, 11, "FILEDIALOG:") == 0;
1794}
1795
1796/////////////////////////////////////////////////////////////////////////////////////
1797/// Create dialog instance to use as embedded dialog inside provided widget
1798/// Loads libROOTBrowserv7 and tries to call RFileDialog::Embedded() method
1799/// Embedded dialog started on the client side where FileDialogController.SaveAs() method called
1800/// Such method immediately send message with "FILEDIALOG:" prefix
1801/// On the server side widget should detect such message and call RFileDialog::Embedded()
1802/// providing received string as second argument.
1803/// Returned instance of shared_ptr<RFileDialog> may be used to assign callback when file is selected
1804
1805bool RWebWindow::EmbedFileDialog(const std::shared_ptr<RWebWindow> &window, unsigned connid, const std::string &args)
1806{
1807 if (!gStartDialogFunc)
1808 gSystem->Load("libROOTBrowserv7");
1809
1810 if (!gStartDialogFunc)
1811 return false;
1812
1813 return gStartDialogFunc(window, connid, args);
1814}
#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
winID h TVirtualViewer3D TVirtualGLPainter p
winID h direct
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t stamp
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t win
Option_t Option_t width
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t height
char name[80]
Definition TGX11.cxx:110
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2468
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
unsigned fMasterConnection
! used master connection
int fMasterChannel
! used master channel
std::shared_ptr< RWebWindow > fMaster
! master window
@ kEmbedded
window will be embedded into other, no extra browser need to be started
void SetHeadless(bool on=true)
set headless mode
static int GetBoolEnv(const std::string &name, int dfl=-1)
Parse boolean gEnv variable which should be "yes" or "no".
Represents web window, which can be shown in web browser or any other supported environment.
bool CheckDataToSend(std::shared_ptr< WebConn > &conn)
Checks if one should send data for specified connection Returns true when send operation was performe...
int WaitFor(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
unsigned GetId() const
Returns ID for the window - unique inside window manager.
std::vector< MasterConn > GetMasterConnections(unsigned connid=0) const
Get list of master connections.
void AddMasterConnection(std::shared_ptr< RWebWindow > window, unsigned connid, int channel)
Add new master connection If there are many connections - only same master is allowed.
std::mutex fConnMutex
! mutex used to protect connection list
WebWindowDataCallback_t fDataCallback
! main callback when data over channel 1 is arrived
void CheckInactiveConnections()
Check if there are connection which are inactive for longer time For instance, batch browser will be ...
unsigned fId
! unique identifier
bool fHasWindowThrd
! indicate if special window thread was started
std::vector< MasterConn > fMasterConns
! master connections
void SetClearOnClose(const std::shared_ptr< void > &handle=nullptr)
Set handle which is cleared when last active connection is closed Typically can be used to destroy we...
void StartThread()
Start special thread which will be used by the window to handle all callbacks One has to be sure,...
unsigned fConnCnt
! counter of new connections to assign ids
unsigned fProtocolConnId
! connection id, which is used for writing protocol
ConnectionsList_t GetWindowConnections(unsigned connid=0, bool only_active=false) const
returns connection list (or all active connections)
bool fSendMT
! true is special threads should be used for sending data
std::thread::id fCallbacksThrdId
! thread id where callbacks should be invoked
std::queue< QueueEntry > fInputQueue
! input queue for all callbacks
void SetConnToken(const std::string &token="")
Configures connection token (default none) When specified, in URL of webpage such token should be pro...
unsigned MakeHeadless(bool create_new=false)
Start headless browser for specified window Normally only single instance is used,...
std::string GetUrl(bool remote=true)
Return URL string to access web window.
void CloseConnections()
Closes all connection to clients Normally leads to closing of all correspondent browser windows Some ...
std::shared_ptr< RWebWindow > fMaster
! master window where this window is embedded
int NumConnections(bool with_pending=false) const
Returns current number of active clients connections.
bool fCallbacksThrdIdSet
! flag indicating that thread id is assigned
std::string fUserArgs
! arbitrary JSON code, which is accessible via conn.getUserArgs() method
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...
unsigned fConnLimit
! number of allowed active connections
void InvokeCallbacks(bool force=false)
Invoke callbacks with existing data Must be called from appropriate thread.
std::string GetClientVersion() const
Returns current client version.
void SetConnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for new connection.
void Sync()
Special method to process all internal activity when window runs in separate thread.
void UseServerThreads()
Let use THttpServer threads to process requests WARNING!!! only for expert use Should be only used wh...
void TerminateROOT()
Terminate ROOT session Tries to correctly close THttpServer, associated with RWebWindowsManager After...
void Send(unsigned connid, const std::string &data)
Sends data to specified connection.
unsigned Show(const RWebDisplayArgs &args="")
Show window in specified location.
THttpServer * GetServer()
Return THttpServer instance serving requests to the window.
unsigned AddDisplayHandle(bool headless_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...
std::vector< std::shared_ptr< WebConn > > ConnectionsList_t
void AssignThreadId()
Assign thread id which has to be used for callbacks WARNING!!! only for expert use Automatically done...
bool IsNativeOnlyConn() const
returns true if only native (own-created) connections are allowed
void SendBinary(unsigned connid, const void *data, std::size_t len)
Send binary data to specified connection.
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
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 AddEmbedWindow(std::shared_ptr< RWebWindow > window, unsigned connid, int channel)
Add embed window.
std::shared_ptr< WebConn > FindConnection(unsigned wsid)
Find connection with specified websocket id.
void SetDisconnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for disconnecting.
std::vector< unsigned > GetConnections(unsigned excludeid=0) const
returns vector with all existing connections ids One also can exclude specified connection from retur...
void SetDataCallBack(WebWindowDataCallback_t func)
Set call-back function for data, received from the clients via websocket.
float fOperationTmout
! timeout in seconds to perform synchronous operation, default 50s
static std::function< bool(const std::shared_ptr< RWebWindow > &, unsigned, const std::string &)> gStartDialogFunc
void SetUserArgs(const std::string &args)
Set arbitrary JSON data, which is accessible via conn.getUserArgs() method in JavaScript This JSON co...
std::string fConnToken
! value of "token" URL parameter which should be provided for connecting window
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...
std::shared_ptr< RWebWindowWSHandler > fWSHandler
! specialize websocket handler for all incoming connections
void StopThread()
Stop special thread.
void SubmitData(unsigned connid, bool txt, std::string &&data, int chid=1)
Internal method to send data.
~RWebWindow()
RWebWindow destructor Closes all connections and remove window from manager.
static bool EmbedFileDialog(const std::shared_ptr< RWebWindow > &window, unsigned connid, const std::string &args)
Create dialog instance to use as embedded dialog inside provided widget Loads libROOTBrowserv7 and tr...
void CloseConnection(unsigned connid)
Close specified connection.
ConnectionsList_t fPendingConn
! list of pending connection with pre-assigned keys
unsigned GetConnectionId(int num=0) const
Returns connection id for specified connection sequence number Only active connections are returned -...
std::string GetConnToken() const
Returns configured connection token.
float GetOperationTmout() const
Returns timeout for synchronous WebWindow operations.
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.
std::string fProtocolPrefix
! prefix for created files names
int GetSendQueueLength(unsigned connid) const
Returns send queue length for specified connection.
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...
std::string fProtocol
! protocol
std::shared_ptr< WebConn > _FindConnWithKey(const std::string &key) const
Find connection with specified key.
bool CanSend(unsigned connid, bool direct=true) const
Returns true if sending via specified connection can be performed.
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 fUseProcessEvents
! all window functionality will run through process events
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 ...
static void SetStartDialogFunc(std::function< bool(const std::shared_ptr< RWebWindow > &, unsigned, const std::string &)>)
Configure func which has to be used for starting dialog.
std::string fPanelName
! panel name which should be shown in the window
void Run(double tm=0.)
Run window functionality for specified time If no action can be performed - just sleep specified time...
std::string GetAddr() const
Returns window address which is used in URL.
std::shared_ptr< RWebWindowsManager > fMgr
! display manager
std::string fProtocolFileName
! local file where communication protocol will be written
ConnectionsList_t fConn
! list of all accepted connections
WebWindowConnectCallback_t fConnCallback
! callback for connect event
void CheckPendingConnections()
Check if started process(es) establish connection.
std::shared_ptr< void > fClearOnClose
! entry which is cleared when last connection is closed
std::mutex fInputQueueMutex
! mutex to protect input queue
std::string _MakeSendHeader(std::shared_ptr< WebConn > &conn, bool txt, const std::string &data, int chid)
Internal method to prepare text part of send data Should be called under locked connection mutex.
std::chrono::time_point< std::chrono::system_clock > timestamp_t
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
std::thread fWindowThrd
! special thread for that window
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)
Complete websocket send operation Clear "doing send" flag and check if next operation has to be start...
bool fUseServerThreads
! indicates that server thread is using, no special window thread
unsigned FindHeadlessConnection()
Returns connection id of window running in headless mode This can be special connection which may run...
int WaitForTimed(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
bool fProcessMT
! if window event processing performed in dedicated thread
int fProtocolCnt
! counter for protocol recording
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 RemoveMasterConnection(unsigned connid=0)
Remove master connection - if any.
void RemoveEmbedWindow(unsigned connid, int channel)
Remove RWebWindow associated with the channelfEmbed.
void SetCallBacks(WebWindowConnectCallback_t conn, WebWindowDataCallback_t data, WebWindowConnectCallback_t disconn=nullptr)
Set call-backs function for connect, data and disconnect events.
std::string GenerateKey() const
Generate new unique key for the window.
WebWindowConnectCallback_t fDisconnCallback
! callback for disconnect event
unsigned GetMaxQueueLength() const
Return maximal queue length of data which can be held by window.
static bool IsFileDialogMessage(const std::string &msg)
Check if this could be the message send by client to start new file dialog If returns true,...
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.
Contains arguments for single HTTP call.
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
Online http server for arbitrary ROOT application.
Definition THttpServer.h:31
Random number generator class based on M.
Definition TRandom3.h:27
void SetSeed(ULong_t seed=0) override
Set the random generator sequence if seed is 0 (default value) a TUUID is generated and used to fill ...
Definition TRandom3.cxx:206
virtual UInt_t Integer(UInt_t imax)
Returns a random integer uniformly distributed on the interval [ 0, imax-1 ].
Definition TRandom.cxx:360
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
Definition TSystem.cxx:1842
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:660
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:683
const Int_t n
Definition legend1.C:16
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
std::function< void(unsigned, const std::string &)> WebWindowDataCallback_t
function signature for call-backs from the window clients first argument is connection id,...
ROOT::Experimental::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< 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
bool fText
! is text data
std::shared_ptr< THttpCallArg > fHold
! request used to hold headless browser
~WebConn()
Destructor for WebConn Notify special HTTP request which blocks headless browser from exit.