33using namespace std::string_literals;
42 fHold->SetTextContent(
"console.log('execute holder script'); if (window) setTimeout (window.close, 1000); if (window) window.close();");
43 fHold->NotifyCondition();
98 for (
auto &conn : lst) {
99 conn->fActive =
false;
100 for (
auto &elem: conn->fEmbed)
101 elem.second->fMaster.reset();
104 fMgr->Unregister(*
this);
117 if (!
fConn.empty()) {
118 R__ERROR_HERE(
"webgui") <<
"Cannot configure panel when connection exists";
130std::shared_ptr<ROOT::Experimental::RWebWindowWSHandler>
149 return fMgr->GetUrl(*
this, remote);
157 return fMgr->GetServer();
167 return fMgr->ShowWindow(*
this,
false, args);
182 connid =
fMgr->ShowWindow(*
this,
true, args);
196 if (entry->fBatchMode)
197 return entry->fConnId;
200 for (
auto &conn :
fConn) {
201 if (conn->fBatchMode)
202 return conn->fConnId;
219 if (!entry->fBatchMode)
220 return entry->fConnId;
223 for (
auto &conn :
fConn) {
224 if (!conn->fBatchMode)
225 return conn->fConnId;
239 for (
auto &conn :
fConn) {
240 if (conn->fWSId == wsid)
248 std::shared_ptr<WebConn> key;
249 std::string keyvalue;
258 if (!keyvalue.empty())
270 fConn.emplace_back(key);
272 fConn.emplace_back(std::make_shared<WebConn>(++
fConnCnt, wsid));
285 std::shared_ptr<WebConn> res;
290 for (
size_t n = 0;
n <
fConn.size(); ++
n)
291 if (
fConn[
n]->fWSId == wsid) {
292 res = std::move(
fConn[
n]);
294 res->fActive =
false;
300 for (
auto &elem: res->fEmbed)
301 elem.second->fMaster.reset();
313 std::string query = arg->GetQuery();
315 if (query.compare(0, 4,
"key=") != 0)
318 std::string key = query.substr(4);
320 std::shared_ptr<THttpCallArg> prev;
322 bool found_key =
false;
328 if (entry->fKey == key) {
331 prev = std::move(entry->fHold);
337 for (
auto &conn :
fConn) {
338 if (conn->fKey == key) {
340 prev = std::move(conn->fHold);
348 prev->SetTextContent(
"console.log('execute holder script'); if (window) window.close();");
349 prev->NotifyCondition();
388 connid = entry.fConnId;
390 arg = std::move(entry.fData);
423 auto conn = std::make_shared<WebConn>(
fConnCnt, batch_mode, key);
440 if (entry->fKey == key)
444 for (
auto &conn :
fConn) {
445 if (conn->fKey == key)
460 timestamp_t stamp = std::chrono::system_clock::now();
462 float tmout =
fMgr->GetLaunchTmout();
469 auto pred = [&](std::shared_ptr<WebConn> &
e) {
470 std::chrono::duration<double> diff = stamp -
e->fSendStamp;
472 if (diff.count() > tmout) {
473 R__DEBUG_HERE(
"webgui") <<
"Halt process after " << diff.count() <<
" sec";
474 selected.emplace_back(
e);
493 timestamp_t stamp = std::chrono::system_clock::now();
495 double batch_tmout = 20.;
497 std::vector<std::shared_ptr<WebConn>> clr;
502 auto pred = [&](std::shared_ptr<WebConn> &conn) {
503 std::chrono::duration<double> diff = stamp - conn->fSendStamp;
505 if ((diff.count() > batch_tmout) && conn->fBatchMode) {
506 conn->fActive =
false;
507 clr.emplace_back(conn);
516 for (
auto &entry : clr)
546 R__ERROR_HERE(
"webgui") <<
"WSHandle with given websocket id " << arg.
GetWSId() <<
" already exists";
572 R__ERROR_HERE(
"webgui") <<
"Get websocket data without valid connection - ignore!!!";
583 char *str_end =
nullptr;
585 unsigned long ackn_oper = std::strtoul(buf, &str_end, 10);
586 if (!str_end || *str_end !=
':') {
587 R__ERROR_HERE(
"webgui") <<
"missing number of acknowledged operations";
591 unsigned long can_send = std::strtoul(str_end + 1, &str_end, 10);
592 if (!str_end || *str_end !=
':') {
597 unsigned long nchannel = std::strtoul(str_end + 1, &str_end, 10);
598 if (!str_end || *str_end !=
':') {
603 Long_t processed_len = (str_end + 1 - buf);
612 timestamp_t stamp = std::chrono::system_clock::now();
615 std::lock_guard<std::mutex> grd(conn->fMutex);
617 conn->fSendCredits += ackn_oper;
619 conn->fClientCredits = (
int)can_send;
620 conn->fRecvStamp = stamp;
628 if ((nchannel != 0) || (cdata.find(
"READY=") == 0)) {
641 if ((cdata.find(
"READY=") == 0) && !conn->fReady) {
642 std::string key = cdata.substr(6);
649 if (!key.empty() && !conn->fKey.empty() && (conn->fKey != key)) {
650 R__ERROR_HERE(
"webgui") <<
"Key mismatch after established connection " << key <<
" != " << conn->fKey;
663 }
else if (cdata.compare(0,8,
"CLOSECH=") == 0) {
664 int channel = std::stoi(cdata.substr(8));
665 auto iter = conn->fEmbed.find(channel);
666 if (iter != conn->fEmbed.end()) {
668 conn->fEmbed.erase(iter);
671 }
else if (
fPanelName.length() && (conn->fReady < 10)) {
672 if (cdata ==
"PANEL_READY") {
680 }
else if (nchannel == 1) {
682 }
else if (nchannel > 1) {
684 auto embed_window = conn->fEmbed[nchannel];
686 embed_window->ProvideQueueEntry(conn->fConnId,
kind_Data, std::move(cdata));
702 std::lock_guard<std::mutex> grd(conn->fMutex);
703 conn->fDoingSend =
false;
718 R__ERROR_HERE(
"webgui") <<
"try to send text data when connection not established";
722 if (conn->fSendCredits <= 0) {
723 R__ERROR_HERE(
"webgui") <<
"No credits to send text data via connection";
727 if (conn->fDoingSend) {
728 R__ERROR_HERE(
"webgui") <<
"Previous send operation not completed yet";
733 buf.reserve(data.length() + 100);
735 buf.append(std::to_string(conn->fRecvCount));
737 buf.append(std::to_string(conn->fSendCredits));
739 conn->fRecvCount = 0;
740 conn->fSendCredits--;
742 buf.append(std::to_string(chid));
747 }
else if (data.length()==0) {
748 buf.append(
"$$nullbinary$$");
750 buf.append(
"$$binary$$");
762 std::string hdr, data;
765 std::lock_guard<std::mutex> grd(conn->fMutex);
767 if (!conn->fActive || (conn->fSendCredits <= 0) || conn->fDoingSend)
return false;
769 if (!conn->fQueue.empty()) {
772 if (!hdr.empty() && !item.
fText)
773 data = std::move(item.
fData);
775 }
else if ((conn->fClientCredits < 3) && (conn->fRecvCount > 1)) {
780 if (hdr.empty())
return false;
782 conn->fDoingSend =
true;
788 res =
fWSHandler->SendCharStarWS(conn->fWSId, hdr.c_str());
790 res =
fWSHandler->SendHeaderWS(conn->fWSId, hdr.c_str(), data.data(), data.length());
794 if (res >=0)
return true;
798 std::lock_guard<std::mutex> grd(conn->fMutex);
799 conn->fDoingSend =
false;
816 for (
auto &conn : arr)
822 }
while (!only_once);
854 if (
fMgr != win->fMgr) {
855 R__ERROR_HERE(
"WebDisplay") <<
"Same web window manager should be used";
859 std::string res(
"../");
860 res.append(win->GetAddr());
913 auto sz =
fConn.size();
944 return ((num >= 0) && (num < (
int)
fConn.size()) &&
fConn[num]->fActive) ?
fConn[num]->fConnId : 0;
956 for (
auto &conn :
fConn) {
957 if (connid && (conn->fConnId != connid))
959 if (conn->fActive || !only_active)
965 if (!connid || (conn->fConnId == connid))
1002 for (
auto &conn :
fConn) {
1003 if ((conn->fActive || !only_active) && (!connid || (conn->fConnId == connid)))
1004 arr.push_back(conn);
1009 if (!connid || (conn->fConnId == connid))
1010 arr.push_back(conn);
1027 for (
auto &conn : arr) {
1029 std::lock_guard<std::mutex> grd(conn->fMutex);
1031 if (direct && (!conn->fQueue.empty() || (conn->fSendCredits == 0) || conn->fDoingSend))
1034 if (conn->fQueue.size() >= maxqlen)
1051 std::lock_guard<std::mutex> grd(conn->fMutex);
1052 int len = conn->fQueue.size();
1053 if (len > maxq) maxq = len;
1071 auto cnt = arr.size();
1074 timestamp_t stamp = std::chrono::system_clock::now();
1076 for (
auto &conn : arr) {
1082 fname.append(
"msg");
1084 fname.append(txt ?
".txt" :
".bin");
1086 std::ofstream ofs(fname);
1087 ofs.write(data.c_str(), data.length());
1099 conn->fSendStamp = stamp;
1101 std::lock_guard<std::mutex> grd(conn->fMutex);
1103 if (conn->fQueue.size() < maxqlen) {
1105 conn->fQueue.emplace(chid, txt, std::string(data));
1107 conn->fQueue.emplace(chid, txt, std::move(data));
1122 SubmitData(connid,
true, std::string(data), 1);
1131 SubmitData(connid,
false, std::move(data), 1);
1142 std::copy((
const char *)data, (
const char *)data + len, buf.begin());
1143 SubmitData(connid,
false, std::move(buf), 1);
1155 }
else if (
fMgr->IsUseHttpThread()) {
1157 R__ERROR_HERE(
"webgui") <<
"create web window from main thread when THttpServer created with special thread - not supported";
1227 return fMgr->WaitFor(*
this, check);
1255 return fMgr->WaitFor(*
this, check,
true, duration);
1266 R__WARNING_HERE(
"webgui") <<
"Change thread id where RWebWindow is executed";
1288 if (arr.size() == 0)
1292 if (arr[0]->fEmbed.find(channel) != arr[0]->fEmbed.end())
1295 arr[0]->fEmbed[channel] = window;
1297 return arr[0]->fConnId;
1307 for (
auto &conn : arr) {
1308 auto iter = conn->fEmbed.find(channel);
1309 if (iter != conn->fEmbed.end())
1310 conn->fEmbed.erase(iter);
1348 window->fMaster = args.
fMaster;
1349 window->fMasterConnId = connid;
1362 return window->Show(args);
#define R__ERROR_HERE(GROUP)
#define R__WARNING_HERE(GROUP)
#define R__DEBUG_HERE(GROUP)
char * Form(const char *fmt,...)
Holds different arguments for starting browser with RWebDisplayHandle::Display() method.
int fMasterChannel
! used master channel
EBrowserKind GetBrowserKind() const
returns configured browser kind, see EBrowserKind for supported values
std::shared_ptr< RWebWindow > fMaster
! master window
@ kEmbedded
window will be embedded into other, no extra browser need to be started
std::vector< std::shared_ptr< WebConn > > ConnectionsList_t
void RemoveEmbedWindow(unsigned connid, int channel)
Remove RWebWindow associated with the channel.
std::shared_ptr< RWebWindow > fMaster
! master window where this window is embeded
int WaitForTimed(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
std::shared_ptr< WebConn > FindOrCreateConnection(unsigned wsid, bool make_new, const char *query)
Find connection with given websocket id Connection mutex should be locked before method calling.
std::string fUserArgs
! arbitrary JSON code, which is accessible via conn.getUserArgs() method
bool CheckDataToSend(std::shared_ptr< WebConn > &conn)
Checks if one should send data for specified connection Returns true when send operation was performe...
int fMasterChannel
! channel id in the master window
float GetOperationTmout() const
Returns timeout for synchronous WebWindow operations.
bool ProcessWS(THttpCallArg &arg)
Processing of websockets call-backs, invoked from RWebWindowWSHandler Method invoked from http server...
std::shared_ptr< WebConn > FindConnection(unsigned wsid)
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...
void Run(double tm=0.)
Run window functionality for specified time If no action can be performed - just sleep specified time...
void SetUserArgs(const std::string &args)
Set arbitrary JSON code, which is accessible via conn.GetUserArgs() method This JSON code injected in...
unsigned GetId() const
Returns ID for the window - unique inside window manager.
void CloseConnection(unsigned connid)
Close specified connection Connection id usually appears in the correspondent call-backs.
void CheckInactiveConnections()
Check if there are connection which are inactive for longer time For instance, batch browser will be ...
ConnectionsList_t fConn
! list of all accepted connections
unsigned FindBatch()
Returns connection id of batch job Connection to that job may not be initialized yet If connection do...
std::string fProtocolPrefix
! prefix for created files names
WebWindowConnectCallback_t fConnCallback
! callback for connect event
unsigned GetMaxQueueLength() const
Return maximal queue length of data which can be held by window.
bool HasKey(const std::string &key) const
Returns true if provided key value already exists (in processes map or in existing connections)
void SetDisconnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for disconnecting.
unsigned fConnLimit
! number of allowed active 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...
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...
void Send(unsigned connid, const std::string &data)
Sends data to specified connection If connid==0, data will be send to all connections.
std::string _MakeSendHeader(std::shared_ptr< WebConn > &conn, bool txt, const std::string &data, int chid)
Prepare text part of send data Should be called under locked connection mutex.
unsigned GetConnectionId(int num=0) const
Returns connection for specified connection number Only active connections are returned - where clien...
unsigned fMasterConnId
! master connection id
bool fSendMT
! true is special threads should be used for sending data
int WaitFor(WebWindowWaitFunc_t check)
Waits until provided check function or lambdas returns non-zero value Check function has following si...
std::thread::id fCallbacksThrdId
! thread id where callbacks should be invoked
std::chrono::time_point< std::chrono::system_clock > timestamp_t
std::string fClientVersion
! configured client version, used as prefix in scripts URL
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...
unsigned fConnCnt
! counter of new connections to assign ids
THttpServer * GetServer()
Return THttpServer instance serving requests to the window.
std::string fPanelName
! panel name which should be shown in the window
unsigned fProtocolConnId
! connection id, which is used for writing protocol
void TerminateROOT()
Terminate ROOT session Tries to correctly close THttpServer, associated with RWebWindowsManager After...
void SetDataCallBack(WebWindowDataCallback_t func)
Set call-back function for data, received from the clients via websocket.
std::string GetUserArgs() const
Returns configured user arguments for web window See SetUserArgs method for more details.
WebWindowDataCallback_t fDataCallback
! main callback when data over channel 1 is arrived
void Sync()
Special method to process all internal activity when window runs in separate thread.
ConnectionsList_t fPendingConn
! list of pending connection with pre-assigned keys
void InvokeCallbacks(bool force=false)
Invoke callbacks with existing data Must be called from appropriate thread.
std::string GetUrl(bool remote=true)
Return URL string to access web window If remote flag is specified, real HTTP server will be started ...
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,...
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...
unsigned GetDisplayConnection() const
Returns first connection id where window is displayed It could be that connection(s) not yet fully es...
void SetCallBacks(WebWindowConnectCallback_t conn, WebWindowDataCallback_t data, WebWindowConnectCallback_t disconn=nullptr)
Set call-backs function for connect, data and disconnect events.
std::string GetAddr() const
Returns window address which is used in URL.
int GetSendQueueLength(unsigned connid) const
returns send queue length for specified connection if connid==0, maximal value for all connections is...
void ProvideQueueEntry(unsigned connid, EQueueEntryKind kind, std::string &&arg)
Provide data to user callback User callback must be executed in the window thread.
void AssignCallbackThreadId()
Assign thread id which has to be used for callbacks.
RWebWindow()
RWebWindow constructor Should be defined here because of std::unique_ptr<RWebWindowWSHandler>
std::shared_ptr< RWebWindowWSHandler > fWSHandler
! specialize websocket handler for all incoming connections
std::string fProtocolFileName
! local file where communication protocol will be written
bool CanSend(unsigned connid, bool direct=true) const
returns true if sending via specified connection can be performed if direct==true,...
std::string GetRelativeAddr(const std::shared_ptr< RWebWindow > &win) const
Returns relative URL address for the specified window Address can be required if one needs to access ...
void SetPanelName(const std::string &name)
Configure window to show some of existing JSROOT panels It uses "file:rootui5sys/panel/panel....
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 SetClientVersion(const std::string &vers)
Set client version, used as prefix in scripts URL When changed, web browser will reload all related J...
std::shared_ptr< RWebWindowsManager > fMgr
! display manager
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, int channel)
Add embed window.
void CompleteWSSend(unsigned wsid)
std::mutex fInputQueueMutex
! mutex to protect input queue
void SetConnectCallBack(WebWindowConnectCallback_t func)
Set call-back function for new connection.
ConnectionsList_t GetConnections(unsigned connid=0, bool only_active=false) const
returns connection (or all active connections)
bool IsNativeOnlyConn() const
returns true if only native (own-created) connections are allowed
int fProtocolCnt
! counter for protocol recording
std::queue< QueueEntry > fInputQueue
! input queue for all callbacks
void SendBinary(unsigned connid, const void *data, std::size_t len)
Send binary data to specified connection If connid==0, data will be sent to all connections.
bool fProcessMT
! if window event processing performed in dedicated thread
std::string fProtocol
! protocol
static std::shared_ptr< RWebWindow > Create()
Create new RWebWindow Using default RWebWindowsManager.
std::shared_ptr< WebConn > RemoveConnection(unsigned wsid)
Remove connection with given websocket id.
unsigned fId
! unique identifier
float fOperationTmout
! timeout in seconds to perform synchronous operation, default 50s
WebWindowConnectCallback_t fDisconnCallback
! callback for disconnect event
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...
static unsigned ShowWindow(std::shared_ptr< RWebWindow > window, const RWebDisplayArgs &args="")
Static method to show web window Has to be used instead of RWebWindow::Show() when window potentially...
void CheckPendingConnections()
Check if started process(es) establish connection.
std::string GetClientVersion() const
Returns current client version.
std::mutex fConnMutex
! mutex used to protect connection list
int NumConnections(bool with_pending=false) const
Returns current number of active clients connections.
static std::shared_ptr< RWebWindowsManager > & Instance()
Returns default window manager Used to display all standard ROOT elements like TCanvas or TFitPanel.
static bool IsMainThrd()
Returns true when called from main process Main process recognized at the moment when library is load...
UInt_t GetWSId() const
get web-socket id
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.
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
void SetOptions(const char *opt)
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
void swap(RDirectoryEntry &e1, RDirectoryEntry &e2) noexcept
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...
static constexpr double s
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