Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
THttpServer.cxx
Go to the documentation of this file.
1// $Id$
2// Author: Sergey Linev 21/12/2013
3
4/*************************************************************************
5 * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "THttpServer.h"
13
14#include "TThread.h"
15#include "TTimer.h"
16#include "TSystem.h"
17#include "TROOT.h"
18#include "TUrl.h"
19#include "TEnv.h"
20#include "TError.h"
21#include "TClass.h"
22#include "RConfigure.h"
23#include "TRegexp.h"
24#include "TObjArray.h"
25#include "ROOT/RMakeUnique.hxx"
26
27#include "THttpEngine.h"
28#include "THttpLongPollEngine.h"
29#include "THttpWSHandler.h"
30#include "TRootSniffer.h"
31#include "TRootSnifferStore.h"
32#include "TCivetweb.h"
33#include "TFastCgi.h"
34
35#include <string>
36#include <cstdlib>
37#include <cstring>
38#include <fstream>
39#include <chrono>
40
41////////////////////////////////////////////////////////////////////////////////
42
43//////////////////////////////////////////////////////////////////////////
44// //
45// THttpTimer //
46// //
47// Specialized timer for THttpServer //
48// Provides regular calls of THttpServer::ProcessRequests() method //
49// //
50//////////////////////////////////////////////////////////////////////////
51
52class THttpTimer : public TTimer {
53public:
54 THttpServer &fServer; ///!< server processing requests
55
56 /// constructor
57 THttpTimer(Long_t milliSec, Bool_t mode, THttpServer &serv) : TTimer(milliSec, mode), fServer(serv) {}
58
59 /// timeout handler
60 /// used to process http requests in main ROOT thread
61 void Timeout() override { fServer.ProcessRequests(); }
62};
63
64//////////////////////////////////////////////////////////////////////////////////////////////////
65
66//////////////////////////////////////////////////////////////////////////
67// //
68// THttpServer //
69// //
70// Online http server for arbitrary ROOT application //
71// //
72// Idea of THttpServer - provide remote http access to running //
73// ROOT application and enable HTML/JavaScript user interface. //
74// Any registered object can be requested and displayed in the browser. //
75// There are many benefits of such approach: //
76// * standard http interface to ROOT application //
77// * no any temporary ROOT files when access data //
78// * user interface running in all browsers //
79// //
80// Starting HTTP server //
81// //
82// To start http server, at any time create instance //
83// of the THttpServer class like: //
84// serv = new THttpServer("http:8080"); //
85// //
86// This will starts civetweb-based http server with http port 8080. //
87// Than one should be able to open address "http://localhost:8080" //
88// in any modern browser (IE, Firefox, Chrome) and browse objects, //
89// created in application. By default, server can access files, //
90// canvases and histograms via gROOT pointer. All such objects //
91// can be displayed with JSROOT graphics. //
92// //
93// At any time one could register other objects with the command: //
94// //
95// TGraph* gr = new TGraph(10); //
96// gr->SetName("gr1"); //
97// serv->Register("graphs/subfolder", gr); //
98// //
99// If objects content is changing in the application, one could //
100// enable monitoring flag in the browser - than objects view //
101// will be regularly updated. //
102// //
103// More information: https://root.cern/root/htmldoc/guides/HttpServer/HttpServer.html //
104// //
105//////////////////////////////////////////////////////////////////////////
106
108
109////////////////////////////////////////////////////////////////////////////////
110/// constructor
111///
112/// As argument, one specifies engine kind which should be
113/// created like "http:8080". One could specify several engines
114/// at once, separating them with semicolon (";"). Following engines are supported:
115///
116/// http - TCivetweb, civetweb-based implementation of http protocol
117/// fastcgi - TFastCgi, special protocol for communicating with web servers
118///
119/// For each created engine one should provide socket port number like "http:8080" or "fastcgi:9000".
120/// Additional engine-specific options can be supplied with URL syntax like "http:8080?thrds=10".
121/// Full list of supported options should be checked in engines docu.
122///
123/// One also can configure following options, separated by semicolon:
124///
125/// readonly, ro - set read-only mode (default)
126/// readwrite, rw - allows methods execution of registered objects
127/// global - scans global ROOT lists for existing objects (default)
128/// noglobal - disable scan of global lists
129/// cors - enable CORS header with origin="*"
130/// cors=domain - enable CORS header with origin="domain"
131/// basic_sniffer - use basic sniffer without support of hist, gpad, graph classes
132///
133/// For example, create http server, which allows cors headers and disable scan of global lists,
134/// one should provide "http:8080;cors;noglobal" as parameter
135///
136/// THttpServer uses JavaScript ROOT (https://root.cern/js) to implement web clients UI.
137/// Normally JSROOT sources are used from $ROOTSYS/js directory,
138/// but one could set JSROOTSYS shell variable to specify alternative location
139
140THttpServer::THttpServer(const char *engine) : TNamed("http", "ROOT http server")
141{
142 const char *jsrootsys = gSystem->Getenv("JSROOTSYS");
143 if (!jsrootsys)
144 jsrootsys = gEnv->GetValue("HttpServ.JSRootPath", jsrootsys);
145
146 if (jsrootsys && *jsrootsys) {
147 if ((strncmp(jsrootsys, "http://", 7)==0) || (strncmp(jsrootsys, "https://", 8)==0))
148 fJSROOT = jsrootsys;
149 else
150 fJSROOTSYS = jsrootsys;
151 }
152
153 if (fJSROOTSYS.Length() == 0) {
154 TString jsdir = TString::Format("%s/js", TROOT::GetDataDir().Data());
155 if (gSystem->ExpandPathName(jsdir)) {
156 ::Warning("THttpServer::THttpServer", "problems resolving '%s', set JSROOTSYS to proper JavaScript ROOT location",
157 jsdir.Data());
158 fJSROOTSYS = ".";
159 } else {
160 fJSROOTSYS = jsdir;
161 }
162 }
163
164 AddLocation("currentdir/", ".");
165 AddLocation("jsrootsys/", fJSROOTSYS.Data());
166 AddLocation("rootsys/", TROOT::GetRootSys());
167
168 fDefaultPage = fJSROOTSYS + "/files/online.htm";
169 fDrawPage = fJSROOTSYS + "/files/draw.htm";
170
171 TRootSniffer *sniff = nullptr;
172 if (strstr(engine, "basic_sniffer")) {
173 sniff = new TRootSniffer("sniff");
174 sniff->SetScanGlobalDir(kFALSE);
175 sniff->CreateOwnTopFolder(); // use dedicated folder
176 } else {
177 sniff = (TRootSniffer *)gROOT->ProcessLineSync("new TRootSnifferFull(\"sniff\");");
178 }
179
180 SetSniffer(sniff);
181
182 // start timer
183 SetTimer(20, kTRUE);
184
185 if (strchr(engine, ';') == 0) {
186 CreateEngine(engine);
187 } else {
188 TObjArray *lst = TString(engine).Tokenize(";");
189
190 for (Int_t n = 0; n <= lst->GetLast(); n++) {
191 const char *opt = lst->At(n)->GetName();
192 if ((strcmp(opt, "readonly") == 0) || (strcmp(opt, "ro") == 0)) {
194 } else if ((strcmp(opt, "readwrite") == 0) || (strcmp(opt, "rw") == 0)) {
196 } else if (strcmp(opt, "global") == 0) {
198 } else if (strcmp(opt, "noglobal") == 0) {
200 } else if (strncmp(opt, "cors=", 5) == 0) {
201 SetCors(opt + 5);
202 } else if (strcmp(opt, "cors") == 0) {
203 SetCors("*");
204 } else
205 CreateEngine(opt);
206 }
207
208 delete lst;
209 }
210}
211
212////////////////////////////////////////////////////////////////////////////////
213/// destructor
214/// delete all http engines and sniffer
215
217{
219
220 if (fTerminated) {
221 TIter iter(&fEngines);
222 while (auto engine = dynamic_cast<THttpEngine *>(iter()))
223 engine->Terminate();
224 }
225
227
228 SetSniffer(nullptr);
229
230 SetTimer(0);
231}
232
233////////////////////////////////////////////////////////////////////////////////
234/// Set TRootSniffer to the server
235/// Server takes ownership over sniffer
236
238{
239 fSniffer.reset(sniff);
240}
241
242////////////////////////////////////////////////////////////////////////////////
243/// Set termination flag,
244/// No any further requests will be processed, server only can be destroyed afterwards
245
247{
249}
250
251////////////////////////////////////////////////////////////////////////////////
252/// returns read-only mode
253
255{
256 return fSniffer ? fSniffer->IsReadOnly() : kTRUE;
257}
258
259////////////////////////////////////////////////////////////////////////////////
260/// Set read-only mode for the server (default on)
261/// In read-only server is not allowed to change any ROOT object, registered to the server
262/// Server also cannot execute objects method via exe.json request
263
265{
266 if (fSniffer)
267 fSniffer->SetReadOnly(readonly);
268}
269
270////////////////////////////////////////////////////////////////////////////////
271/// returns true if only websockets are handled by the server
272/// Typically used by WebGui
273
275{
276 return fWSOnly;
277}
278
279////////////////////////////////////////////////////////////////////////////////
280/// Set websocket-only mode. If true, server will only handle websockets connection
281/// plus serving file requests to access jsroot/ui5 scripts
282
284{
285 fWSOnly = on;
286}
287
288////////////////////////////////////////////////////////////////////////////////
289/// add files location, which could be used in the server
290/// one could map some system folder to the server like AddLocation("mydir/","/home/user/specials");
291/// Than files from this directory could be addressed via server like
292/// http://localhost:8080/mydir/myfile.root
293
294void THttpServer::AddLocation(const char *prefix, const char *path)
295{
296 if (!prefix || (*prefix == 0))
297 return;
298
299 if (!path)
300 fLocations.erase(fLocations.find(prefix));
301 else
302 fLocations[prefix] = path;
303}
304
305////////////////////////////////////////////////////////////////////////////////
306/// Set location of JSROOT to use with the server
307/// One could specify address like:
308/// https://root.cern.ch/js/5.6.3/
309/// http://jsroot.gsi.de/5.6.3/
310/// This allows to get new JSROOT features with old server,
311/// reduce load on THttpServer instance, also startup time can be improved
312/// When empty string specified (default), local copy of JSROOT is used (distributed with ROOT)
313
314void THttpServer::SetJSROOT(const char *location)
315{
316 fJSROOT = location ? location : "";
317}
318
319////////////////////////////////////////////////////////////////////////////////
320/// Set file name of HTML page, delivered by the server when
321/// http address is opened in the browser.
322/// By default, $ROOTSYS/js/files/online.htm page is used
323/// When empty filename is specified, default page will be used
324
325void THttpServer::SetDefaultPage(const std::string &filename)
326{
327 if (!filename.empty())
328 fDefaultPage = filename;
329 else
330 fDefaultPage = fJSROOTSYS + "/files/online.htm";
331
332 // force to read page content next time again
333 fDefaultPageCont.clear();
334}
335
336////////////////////////////////////////////////////////////////////////////////
337/// Set file name of HTML page, delivered by the server when
338/// objects drawing page is requested from the browser
339/// By default, $ROOTSYS/js/files/draw.htm page is used
340/// When empty filename is specified, default page will be used
341
342void THttpServer::SetDrawPage(const std::string &filename)
343{
344 if (!filename.empty())
345 fDrawPage = filename;
346 else
347 fDrawPage = fJSROOTSYS + "/files/draw.htm";
348
349 // force to read page content next time again
350 fDrawPageCont.clear();
351}
352
353////////////////////////////////////////////////////////////////////////////////
354/// factory method to create different http engines
355/// At the moment two engine kinds are supported:
356/// civetweb (default) and fastcgi
357/// Examples:
358/// "http:8080" or "civetweb:8080" or ":8080" - creates civetweb web server with http port 8080
359/// "fastcgi:9000" - creates fastcgi server with port 9000
360/// One could apply additional parameters, using URL syntax:
361/// "http:8080?thrds=10"
362
364{
365 if (!engine)
366 return kFALSE;
367
368 const char *arg = strchr(engine, ':');
369 if (!arg)
370 return kFALSE;
371
372 TString clname;
373 if (arg != engine)
374 clname.Append(engine, arg - engine);
375
376 THttpEngine *eng = nullptr;
377
378 if ((clname.Length() == 0) || (clname == "http") || (clname == "civetweb")) {
379 eng = new TCivetweb(kFALSE);
380 } else if (clname == "https") {
381 eng = new TCivetweb(kTRUE);
382 } else if (clname == "fastcgi") {
383 eng = new TFastCgi();
384 }
385
386 if (!eng) {
387 // ensure that required engine class exists before we try to create it
388 TClass *engine_class = gROOT->LoadClass(clname.Data());
389 if (!engine_class)
390 return kFALSE;
391
392 eng = (THttpEngine *)engine_class->New();
393 if (!eng)
394 return kFALSE;
395 }
396
397 eng->SetServer(this);
398
399 if (!eng->Create(arg + 1)) {
400 delete eng;
401 return kFALSE;
402 }
403
404 fEngines.Add(eng);
405
406 return kTRUE;
407}
408
409////////////////////////////////////////////////////////////////////////////////
410/// create timer which will invoke ProcessRequests() function periodically
411/// Timer is required to perform all actions in main ROOT thread
412/// Method arguments are the same as for TTimer constructor
413/// By default, sync timer with 100 ms period is created
414///
415/// It is recommended to always use sync timer mode and only change period to
416/// adjust server reaction time. Use of async timer requires, that application regularly
417/// calls gSystem->ProcessEvents(). It happens automatically in ROOT interactive shell.
418/// If milliSec == 0, no timer will be created.
419/// In this case application should regularly call ProcessRequests() method.
420///
421/// Async timer allows to use THttpServer in applications, which does not have explicit
422/// gSystem->ProcessEvents() calls. But be aware, that such timer can interrupt any system call
423/// (like malloc) and can lead to dead locks, especially in multi-threaded applications.
424
426{
427 if (fTimer) {
428 fTimer->Stop();
429 fTimer.reset();
430 }
431 if (milliSec > 0) {
432 if (fOwnThread) {
433 Error("SetTimer", "Server runs already in special thread, therefore no any timer can be created");
434 } else {
435 fTimer = std::make_unique<THttpTimer>(milliSec, mode, *this);
436 fTimer->TurnOn();
437 }
438 }
439}
440
441////////////////////////////////////////////////////////////////////////////////
442/// Creates special thread to process all requests, directed to http server
443///
444/// Should be used with care - only dedicated instance of TRootSniffer is allowed
445/// By default THttpServer allows to access global lists pointers gROOT or gFile.
446/// To be on the safe side, all kind of such access performed from the main thread.
447/// Therefore usage of specialized thread means that no any global pointers will
448/// be accessible by THttpServer
449
451{
452 if (fOwnThread)
453 return;
454
455 SetTimer(0);
456 fMainThrdId = 0;
457 fOwnThread = true;
458
459 std::thread thrd([this] {
460 int nempty = 0;
461 while (fOwnThread && !fTerminated) {
462 int nprocess = ProcessRequests();
463 if (nprocess > 0)
464 nempty = 0;
465 else
466 nempty++;
467 if (nempty > 1000) {
468 nempty = 0;
469 std::this_thread::sleep_for(std::chrono::milliseconds(1));
470 }
471 }
472 });
473
474 fThrd = std::move(thrd);
475}
476
477////////////////////////////////////////////////////////////////////////////////
478/// Stop server thread
479/// Normally called shortly before http server destructor
480
482{
483 if (!fOwnThread)
484 return;
485
486 fOwnThread = false;
487 fThrd.join();
488 fMainThrdId = 0;
489}
490
491////////////////////////////////////////////////////////////////////////////////
492/// Checked that filename does not contains relative path below current directory
493/// Used to prevent access to files below current directory
494
496{
497 if (!fname || (*fname == 0))
498 return kFALSE;
499
500 Int_t level = 0;
501
502 while (*fname != 0) {
503
504 // find next slash or backslash
505 const char *next = strpbrk(fname, "/\\");
506 if (next == 0)
507 return kTRUE;
508
509 // most important - change to parent dir
510 if ((next == fname + 2) && (*fname == '.') && (*(fname + 1) == '.')) {
511 fname += 3;
512 level--;
513 if (level < 0)
514 return kFALSE;
515 continue;
516 }
517
518 // ignore current directory
519 if ((next == fname + 1) && (*fname == '.')) {
520 fname += 2;
521 continue;
522 }
523
524 // ignore slash at the front
525 if (next == fname) {
526 fname++;
527 continue;
528 }
529
530 fname = next + 1;
531 level++;
532 }
533
534 return kTRUE;
535}
536
537////////////////////////////////////////////////////////////////////////////////
538/// Verifies that request is just file name
539/// File names typically contains prefix like "jsrootsys/"
540/// If true, method returns real name of the file,
541/// which should be delivered to the client
542/// Method is thread safe and can be called from any thread
543
544Bool_t THttpServer::IsFileRequested(const char *uri, TString &res) const
545{
546 if (!uri || (*uri == 0))
547 return kFALSE;
548
549 TString fname(uri);
550
551 for (auto &entry : fLocations) {
552 Ssiz_t pos = fname.Index(entry.first.c_str());
553 if (pos == kNPOS)
554 continue;
555 fname.Remove(0, pos + (entry.first.length() - 1));
556 if (!VerifyFilePath(fname.Data()))
557 return kFALSE;
558 res = entry.second.c_str();
559 if ((fname[0] == '/') && (res[res.Length() - 1] == '/'))
560 res.Resize(res.Length() - 1);
561 res.Append(fname);
562 return kTRUE;
563 }
564
565 return kFALSE;
566}
567
568////////////////////////////////////////////////////////////////////////////////
569/// Executes http request, specified in THttpCallArg structure
570/// Method can be called from any thread
571/// Actual execution will be done in main ROOT thread, where analysis code is running.
572
573Bool_t THttpServer::ExecuteHttp(std::shared_ptr<THttpCallArg> arg)
574{
575 if (fTerminated)
576 return kFALSE;
577
578 if ((fMainThrdId != 0) && (fMainThrdId == TThread::SelfId())) {
579 // should not happen, but one could process requests directly without any signaling
580
581 ProcessRequest(arg);
582
583 return kTRUE;
584 }
585
586 // add call arg to the list
587 std::unique_lock<std::mutex> lk(fMutex);
588 fArgs.push(arg);
589 // and now wait until request is processed
590 arg->fCond.wait(lk);
591
592 return kTRUE;
593}
594
595////////////////////////////////////////////////////////////////////////////////
596/// Submit http request, specified in THttpCallArg structure
597/// Contrary to ExecuteHttp, it will not block calling thread.
598/// User should reimplement THttpCallArg::HttpReplied() method
599/// to react when HTTP request is executed.
600/// Method can be called from any thread
601/// Actual execution will be done in main ROOT thread, where analysis code is running.
602/// When called from main thread and can_run_immediately==kTRUE, will be
603/// executed immediately.
604/// Returns kTRUE when was executed.
605
606Bool_t THttpServer::SubmitHttp(std::shared_ptr<THttpCallArg> arg, Bool_t can_run_immediately)
607{
608 if (fTerminated)
609 return kFALSE;
610
611 if (can_run_immediately && (fMainThrdId != 0) && (fMainThrdId == TThread::SelfId())) {
612 ProcessRequest(arg);
613 arg->NotifyCondition();
614 return kTRUE;
615 }
616
617 // add call arg to the list
618 std::unique_lock<std::mutex> lk(fMutex);
619 fArgs.push(arg);
620 return kFALSE;
621}
622
623////////////////////////////////////////////////////////////////////////////////
624/// Process requests, submitted for execution
625/// Returns number of processed requests
626///
627/// Normally invoked by THttpTimer, when somewhere in the code
628/// gSystem->ProcessEvents() is called.
629/// User can call serv->ProcessRequests() directly, but only from main thread.
630/// If special server thread is created, called from that thread
631
633{
634 if (fMainThrdId == 0)
636
637 if (fMainThrdId != TThread::SelfId()) {
638 Error("ProcessRequests", "Should be called only from main ROOT thread");
639 return 0;
640 }
641
642 Int_t cnt = 0;
643
644 std::unique_lock<std::mutex> lk(fMutex, std::defer_lock);
645
646 // first process requests in the queue
647 while (true) {
648 std::shared_ptr<THttpCallArg> arg;
649
650 lk.lock();
651 if (!fArgs.empty()) {
652 arg = fArgs.front();
653 fArgs.pop();
654 }
655 lk.unlock();
656
657 if (!arg)
658 break;
659
660 if (arg->fFileName == "root_batch_holder.js") {
662 continue;
663 }
664
665 fSniffer->SetCurrentCallArg(arg.get());
666
667 try {
668 cnt++;
669 ProcessRequest(arg);
670 fSniffer->SetCurrentCallArg(nullptr);
671 } catch (...) {
672 fSniffer->SetCurrentCallArg(nullptr);
673 }
674
675 arg->NotifyCondition();
676 }
677
678 // regularly call Process() method of engine to let perform actions in ROOT context
679 TIter iter(&fEngines);
680 THttpEngine *engine = nullptr;
681 while ((engine = (THttpEngine *)iter()) != nullptr) {
682 if (fTerminated)
683 engine->Terminate();
684 engine->Process();
685 }
686
687 return cnt;
688}
689
690////////////////////////////////////////////////////////////////////////////////
691/// Method called when THttpServer cannot process request
692/// By default such requests replied with 404 code
693/// One could overwrite with method in derived class to process all kinds of such non-standard requests
694
696{
697 arg->Set404();
698}
699
700////////////////////////////////////////////////////////////////////////////////
701/// Process special http request for root_batch_holder.js script
702/// This kind of requests used to hold web browser running in headless mode
703/// Intentionally requests does not replied immediately
704
705void THttpServer::ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg)
706{
707 auto wsptr = FindWS(arg->GetPathName());
708
709 if (!wsptr || !wsptr->ProcessBatchHolder(arg)) {
710 arg->Set404();
711 arg->NotifyCondition();
712 }
713}
714
715////////////////////////////////////////////////////////////////////////////////
716/// Create summary page with active WS handlers
717
719{
720
721 std::string arr = "[";
722
723 {
724 std::lock_guard<std::mutex> grd(fWSMutex);
725 for (auto &ws : fWSHandlers) {
726 if (arr.length() > 1)
727 arr.append(", ");
728
729 arr.append(Form("{ name: \"%s\", title: \"%s\" }", ws->GetName(), ws->GetTitle()));
730 }
731 }
732
733 arr.append("]");
734
735 std::string res = ReadFileContent((TROOT::GetDataDir() + "/js/files/wslist.htm").Data());
736
737 std::string arg = "\"$$$wslist$$$\"";
738
739 auto pos = res.find(arg);
740 if (pos != std::string::npos)
741 res.replace(pos, arg.length(), arr);
742
743 return res;
744}
745
746////////////////////////////////////////////////////////////////////////////////
747/// Replaces all references like "jsrootsys/..."
748/// Either using pre-configured JSROOT installation from web or
749/// redirect to jsrootsys from the main server path to benefit from browser caching
750
751void THttpServer::ReplaceJSROOTLinks(std::shared_ptr<THttpCallArg> &arg)
752{
753 std::string repl;
754
755 if (fJSROOT.Length() > 0) {
756 repl = "=\"";
757 repl.append(fJSROOT.Data());
758 if (repl.back() != '/')
759 repl.append("/");
760 } else {
761 Int_t cnt = 0;
762 if (arg->fPathName.Length() > 0) cnt++;
763 for (Int_t n = 1; n < arg->fPathName.Length()-1; ++n)
764 if (arg->fPathName[n] == '/') {
765 if (arg->fPathName[n-1] != '/') {
766 cnt++; // normal slash in the middle, count it
767 } else {
768 cnt = 0; // double slash, do not touch such path
769 break;
770 }
771 }
772
773 if (cnt > 0) {
774 repl = "=\"";
775 while (cnt-- >0) repl.append("../");
776 repl.append("jsrootsys/");
777 }
778 }
779
780 if (!repl.empty())
781 arg->ReplaceAllinContent("=\"jsrootsys/", repl);
782}
783
784
785////////////////////////////////////////////////////////////////////////////////
786/// Process single http request
787/// Depending from requested path and filename different actions will be performed.
788/// In most cases information is provided by TRootSniffer class
789
790void THttpServer::ProcessRequest(std::shared_ptr<THttpCallArg> arg)
791{
792 if (fTerminated) {
793 arg->Set404();
794 return;
795 }
796
797 if ((arg->fFileName == "root.websocket") || (arg->fFileName == "root.longpoll")) {
798 ExecuteWS(arg);
799 return;
800 }
801
802 if (arg->fFileName.IsNull() || (arg->fFileName == "index.htm") || (arg->fFileName == "default.htm")) {
803
804 if (arg->fFileName == "default.htm") {
805
806 if (!IsWSOnly())
807 arg->fContent = ReadFileContent((fJSROOTSYS + "/files/online.htm").Data());
808
809 } else {
810 auto wsptr = FindWS(arg->GetPathName());
811
812 auto handler = wsptr.get();
813
814 if (!handler)
815 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
816
817 if (handler) {
818
819 arg->fContent = handler->GetDefaultPageContent().Data();
820
821 if (arg->fContent.find("file:") == 0) {
822 const char *fname = arg->fContent.c_str() + 5;
823 TString resolve;
824 if (!IsFileRequested(fname, resolve)) resolve = fname;
825 arg->fContent = ReadFileContent(resolve.Data());
826 }
827
828 handler->VerifyDefaultPageContent(arg);
829
830 arg->CheckWSPageContent(handler);
831 }
832 }
833
834 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() && IsWSOnly()) {
835 arg->fContent = BuildWSEntryPage();
836 }
837
838 if (arg->fContent.empty() && !IsWSOnly()) {
839
840 if (fDefaultPageCont.empty())
842
843 arg->fContent = fDefaultPageCont;
844 }
845
846 if (arg->fContent.empty()) {
847
848 arg->Set404();
849 } else if (!arg->Is404()) {
850
852
853 const char *hjsontag = "\"$$$h.json$$$\"";
854
855 // add h.json caching
856 if (arg->fContent.find(hjsontag) != std::string::npos) {
857 TString h_json;
858 TRootSnifferStoreJson store(h_json, kTRUE);
859 const char *topname = fTopName.Data();
860 if (arg->fTopName.Length() > 0)
861 topname = arg->fTopName.Data();
862 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
863
864 arg->ReplaceAllinContent(hjsontag, h_json.Data());
865
866 arg->AddNoCacheHeader();
867
868 if (arg->fQuery.Index("nozip") == kNPOS)
869 arg->SetZipping();
870 }
871 arg->SetContentType("text/html");
872 }
873 return;
874 }
875
876 if ((arg->fFileName == "draw.htm") && !IsWSOnly()) {
877 if (fDrawPageCont.empty())
879
880 if (fDrawPageCont.empty()) {
881 arg->Set404();
882 } else {
883 const char *rootjsontag = "\"$$$root.json$$$\"";
884 const char *hjsontag = "\"$$$h.json$$$\"";
885
886 arg->fContent = fDrawPageCont;
887
889
890 if ((arg->fQuery.Index("no_h_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
891 (arg->fContent.find(hjsontag) != std::string::npos)) {
892 TString h_json;
893 TRootSnifferStoreJson store(h_json, kTRUE);
894 const char *topname = fTopName.Data();
895 if (arg->fTopName.Length() > 0)
896 topname = arg->fTopName.Data();
897 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
898
899 arg->ReplaceAllinContent(hjsontag, h_json.Data());
900 }
901
902 if ((arg->fQuery.Index("no_root_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
903 (arg->fContent.find(rootjsontag) != std::string::npos)) {
904 std::string str;
905 if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=23", str))
906 arg->ReplaceAllinContent(rootjsontag, str);
907 }
908 arg->AddNoCacheHeader();
909 if (arg->fQuery.Index("nozip") == kNPOS)
910 arg->SetZipping();
911 arg->SetContentType("text/html");
912 }
913 return;
914 }
915
916 if ((arg->fFileName == "favicon.ico") && arg->fPathName.IsNull()) {
917 arg->SetFile(fJSROOTSYS + "/img/RootIcon.ico");
918 return;
919 }
920
921 TString filename;
922 if (IsFileRequested(arg->fFileName.Data(), filename)) {
923 arg->SetFile(filename);
924 return;
925 }
926
927 // check if websocket handler may serve file request
928 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
929 TString wsname = arg->fPathName, fname;
930 auto pos = wsname.First('/');
931 if (pos == kNPOS) {
932 wsname = arg->fPathName;
933 } else {
934 wsname = arg->fPathName(0, pos);
935 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
936 fname.Append("/");
937 }
938
939 fname.Append(arg->fFileName);
940
941 if (VerifyFilePath(fname.Data())) {
942
943 auto ws = FindWS(wsname.Data());
944
945 if (ws && ws->CanServeFiles()) {
946 TString fdir = ws->GetDefaultPageContent();
947 // only when file is specified, can take directory, append prefix and file name
948 if (fdir.Index("file:") == 0) {
949 fdir.Remove(0, 5);
950 auto separ = fdir.Last('/');
951 if (separ != kNPOS)
952 fdir.Resize(separ + 1);
953 else
954 fdir = "./";
955
956 fdir.Append(fname);
957 arg->SetFile(fdir);
958 return;
959 }
960 }
961 }
962 }
963
964 filename = arg->fFileName;
965
966 Bool_t iszip = kFALSE;
967 if (filename.EndsWith(".gz")) {
968 filename.Resize(filename.Length() - 3);
969 iszip = kTRUE;
970 }
971
972 if (IsWSOnly()) {
973 if (arg->fContent.empty())
974 arg->Set404();
975 } else if ((filename == "h.xml") || (filename == "get.xml")) {
976
977 Bool_t compact = arg->fQuery.Index("compact") != kNPOS;
978
979 TString res;
980
981 res.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
982 if (!compact)
983 res.Append("\n");
984 res.Append("<root>");
985 if (!compact)
986 res.Append("\n");
987 {
988 TRootSnifferStoreXml store(res, compact);
989
990 const char *topname = fTopName.Data();
991 if (arg->fTopName.Length() > 0)
992 topname = arg->fTopName.Data();
993 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
994 }
995
996 res.Append("</root>");
997 if (!compact)
998 res.Append("\n");
999
1000 arg->SetContent(std::string(res.Data()));
1001
1002 arg->SetXml();
1003 } else if (filename == "h.json") {
1004 TString res;
1005 TRootSnifferStoreJson store(res, arg->fQuery.Index("compact") != kNPOS);
1006 const char *topname = fTopName.Data();
1007 if (arg->fTopName.Length() > 0)
1008 topname = arg->fTopName.Data();
1009 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1010 arg->SetContent(std::string(res.Data()));
1011 arg->SetJson();
1012 } else if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), arg->fContent)) {
1013 // define content type base on extension
1014 arg->SetContentType(GetMimeType(filename.Data()));
1015 } else {
1016 // miss request, user may process
1017 MissedRequest(arg.get());
1018 }
1019
1020 if (arg->Is404())
1021 return;
1022
1023 if (iszip)
1024 arg->SetZipping(THttpCallArg::kZipAlways);
1025
1026 if (filename == "root.bin") {
1027 // only for binary data master version is important
1028 // it allows to detect if streamer info was modified
1029 const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
1030 arg->AddHeader(parname, Form("%u", (unsigned)fSniffer->GetStreamerInfoHash()));
1031 }
1032
1033 // try to avoid caching on the browser
1034 arg->AddNoCacheHeader();
1035
1036 // potentially add cors header
1037 if (IsCors())
1038 arg->AddHeader("Access-Control-Allow-Origin", GetCors());
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042/// Register object in folders hierarchy
1043///
1044/// See TRootSniffer::RegisterObject() for more details
1045
1046Bool_t THttpServer::Register(const char *subfolder, TObject *obj)
1047{
1048 return fSniffer->RegisterObject(subfolder, obj);
1049}
1050
1051////////////////////////////////////////////////////////////////////////////////
1052/// Unregister object in folders hierarchy
1053///
1054/// See TRootSniffer::UnregisterObject() for more details
1055
1057{
1058 return fSniffer->UnregisterObject(obj);
1059}
1060
1061////////////////////////////////////////////////////////////////////////////////
1062/// Register WS handler to the THttpServer
1063///
1064/// Only such handler can be used in multi-threaded processing of websockets
1065
1066void THttpServer::RegisterWS(std::shared_ptr<THttpWSHandler> ws)
1067{
1068 std::lock_guard<std::mutex> grd(fWSMutex);
1069 fWSHandlers.emplace_back(ws);
1070}
1071
1072////////////////////////////////////////////////////////////////////////////////
1073/// Unregister WS handler to the THttpServer
1074
1075void THttpServer::UnregisterWS(std::shared_ptr<THttpWSHandler> ws)
1076{
1077 std::lock_guard<std::mutex> grd(fWSMutex);
1078 for (int n = (int)fWSHandlers.size(); n > 0; --n)
1079 if ((fWSHandlers[n - 1] == ws) || fWSHandlers[n - 1]->IsDisabled())
1080 fWSHandlers.erase(fWSHandlers.begin() + n - 1);
1081}
1082
1083////////////////////////////////////////////////////////////////////////////////
1084/// Search WS handler with given name
1085///
1086/// Handler must be registered with RegisterWS() method
1087
1088std::shared_ptr<THttpWSHandler> THttpServer::FindWS(const char *name)
1089{
1090 std::lock_guard<std::mutex> grd(fWSMutex);
1091 for (auto &ws : fWSHandlers) {
1092 if (strcmp(name, ws->GetName()) == 0)
1093 return ws;
1094 }
1095
1096 return nullptr;
1097}
1098
1099////////////////////////////////////////////////////////////////////////////////
1100/// Execute WS related operation
1101
1102Bool_t THttpServer::ExecuteWS(std::shared_ptr<THttpCallArg> &arg, Bool_t external_thrd, Bool_t wait_process)
1103{
1104 if (fTerminated) {
1105 arg->Set404();
1106 return kFALSE;
1107 }
1108
1109 auto wsptr = FindWS(arg->GetPathName());
1110
1111 auto handler = wsptr.get();
1112
1113 if (!handler && !external_thrd)
1114 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1115
1116 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1117 std::unique_lock<std::mutex> lk(fMutex);
1118 fArgs.push(arg);
1119 // and now wait until request is processed
1120 if (wait_process)
1121 arg->fCond.wait(lk);
1122
1123 return kTRUE;
1124 }
1125
1126 if (!handler)
1127 return kFALSE;
1128
1129 Bool_t process = kFALSE;
1130
1131 if (arg->fFileName == "root.websocket") {
1132 // handling of web socket
1133 process = handler->HandleWS(arg);
1134 } else if (arg->fFileName == "root.longpoll") {
1135 // ROOT emulation of websocket with polling requests
1136 if (arg->fQuery.BeginsWith("raw_connect") || arg->fQuery.BeginsWith("txt_connect")) {
1137 // try to emulate websocket connect
1138 // if accepted, reply with connection id, which must be used in the following communications
1139 arg->SetMethod("WS_CONNECT");
1140
1141 bool israw = arg->fQuery.BeginsWith("raw_connect");
1142
1143 // automatically assign engine to arg
1144 arg->CreateWSEngine<THttpLongPollEngine>(israw);
1145
1146 if (handler->HandleWS(arg)) {
1147 arg->SetMethod("WS_READY");
1148
1149 if (handler->HandleWS(arg))
1150 arg->SetTextContent(std::string(israw ? "txt:" : "") + std::to_string(arg->GetWSId()));
1151 } else {
1152 arg->TakeWSEngine(); // delete handle
1153 }
1154
1155 process = arg->IsText();
1156 } else {
1157 TUrl url;
1158 url.SetOptions(arg->fQuery);
1159 url.ParseOptions();
1160 const char *connid = url.GetValueFromOptions("connection");
1161 if (connid)
1162 arg->SetWSId(std::stoul(connid));
1163 if (url.HasOption("close")) {
1164 arg->SetMethod("WS_CLOSE");
1165 arg->SetTextContent("OK");
1166 } else {
1167 arg->SetMethod("WS_DATA");
1168 }
1169
1170 process = handler->HandleWS(arg);
1171 }
1172 }
1173
1174 if (!process)
1175 arg->Set404();
1176
1177 return process;
1178}
1179
1180////////////////////////////////////////////////////////////////////////////////
1181/// Restrict access to specified object
1182///
1183/// See TRootSniffer::Restrict() for more details
1184
1185void THttpServer::Restrict(const char *path, const char *options)
1186{
1187 fSniffer->Restrict(path, options);
1188}
1189
1190////////////////////////////////////////////////////////////////////////////////
1191/// Register command which can be executed from web interface
1192///
1193/// As method one typically specifies string, which is executed with
1194/// gROOT->ProcessLine() method. For instance
1195/// serv->RegisterCommand("Invoke","InvokeFunction()");
1196///
1197/// Or one could specify any method of the object which is already registered
1198/// to the server. For instance:
1199/// serv->Register("/", hpx);
1200/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1201/// Here symbols '/->' separates item name from method to be executed
1202///
1203/// One could specify additional arguments in the command with
1204/// syntax like %arg1%, %arg2% and so on. For example:
1205/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1206/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1207/// Such parameter(s) will be requested when command clicked in the browser.
1208///
1209/// Once command is registered, one could specify icon which will appear in the browser:
1210/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1211///
1212/// One also can set extra property '_fastcmd', that command appear as
1213/// tool button on the top of the browser tree:
1214/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1215/// Or it is equivalent to specifying extra argument when register command:
1216/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1217
1218Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1219{
1220 return fSniffer->RegisterCommand(cmdname, method, icon);
1221}
1222
1223////////////////////////////////////////////////////////////////////////////////
1224/// hides folder or element from web gui
1225
1226Bool_t THttpServer::Hide(const char *foldername, Bool_t hide)
1227{
1228 return SetItemField(foldername, "_hidden", hide ? "true" : (const char *)0);
1229}
1230
1231////////////////////////////////////////////////////////////////////////////////
1232/// set name of icon, used in browser together with the item
1233///
1234/// One could use images from $ROOTSYS directory like:
1235/// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
1236
1237Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
1238{
1239 return SetItemField(fullname, "_icon", iconname);
1240}
1241
1242////////////////////////////////////////////////////////////////////////////////
1243
1244Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
1245{
1246 return fSniffer->CreateItem(fullname, title);
1247}
1248
1249////////////////////////////////////////////////////////////////////////////////
1250
1251Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
1252{
1253 return fSniffer->SetItemField(fullname, name, value);
1254}
1255
1256////////////////////////////////////////////////////////////////////////////////
1257
1258const char *THttpServer::GetItemField(const char *fullname, const char *name)
1259{
1260 return fSniffer->GetItemField(fullname, name);
1261}
1262
1263////////////////////////////////////////////////////////////////////////////////
1264/// Returns MIME type base on file extension
1265
1266const char *THttpServer::GetMimeType(const char *path)
1267{
1268 static const struct {
1269 const char *extension;
1270 int ext_len;
1271 const char *mime_type;
1272 } builtin_mime_types[] = {{".xml", 4, "text/xml"},
1273 {".json", 5, "application/json"},
1274 {".bin", 4, "application/x-binary"},
1275 {".gif", 4, "image/gif"},
1276 {".jpg", 4, "image/jpeg"},
1277 {".png", 4, "image/png"},
1278 {".html", 5, "text/html"},
1279 {".htm", 4, "text/html"},
1280 {".shtm", 5, "text/html"},
1281 {".shtml", 6, "text/html"},
1282 {".css", 4, "text/css"},
1283 {".js", 3, "application/x-javascript"},
1284 {".ico", 4, "image/x-icon"},
1285 {".jpeg", 5, "image/jpeg"},
1286 {".svg", 4, "image/svg+xml"},
1287 {".txt", 4, "text/plain"},
1288 {".torrent", 8, "application/x-bittorrent"},
1289 {".wav", 4, "audio/x-wav"},
1290 {".mp3", 4, "audio/x-mp3"},
1291 {".mid", 4, "audio/mid"},
1292 {".m3u", 4, "audio/x-mpegurl"},
1293 {".ogg", 4, "application/ogg"},
1294 {".ram", 4, "audio/x-pn-realaudio"},
1295 {".xslt", 5, "application/xml"},
1296 {".xsl", 4, "application/xml"},
1297 {".ra", 3, "audio/x-pn-realaudio"},
1298 {".doc", 4, "application/msword"},
1299 {".exe", 4, "application/octet-stream"},
1300 {".zip", 4, "application/x-zip-compressed"},
1301 {".xls", 4, "application/excel"},
1302 {".tgz", 4, "application/x-tar-gz"},
1303 {".tar", 4, "application/x-tar"},
1304 {".gz", 3, "application/x-gunzip"},
1305 {".arj", 4, "application/x-arj-compressed"},
1306 {".rar", 4, "application/x-arj-compressed"},
1307 {".rtf", 4, "application/rtf"},
1308 {".pdf", 4, "application/pdf"},
1309 {".swf", 4, "application/x-shockwave-flash"},
1310 {".mpg", 4, "video/mpeg"},
1311 {".webm", 5, "video/webm"},
1312 {".mpeg", 5, "video/mpeg"},
1313 {".mov", 4, "video/quicktime"},
1314 {".mp4", 4, "video/mp4"},
1315 {".m4v", 4, "video/x-m4v"},
1316 {".asf", 4, "video/x-ms-asf"},
1317 {".avi", 4, "video/x-msvideo"},
1318 {".bmp", 4, "image/bmp"},
1319 {".ttf", 4, "application/x-font-ttf"},
1320 {".woff", 5, "font/woff"},
1321 {".woff2", 6, "font/woff2"},
1322 {NULL, 0, NULL}};
1323
1324 int path_len = strlen(path);
1325
1326 for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
1327 if (path_len <= builtin_mime_types[i].ext_len)
1328 continue;
1329 const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
1330 if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
1331 return builtin_mime_types[i].mime_type;
1332 }
1333 }
1334
1335 return "text/plain";
1336}
1337
1338////////////////////////////////////////////////////////////////////////////////
1339/// \deprecated reads file content
1340
1341char *THttpServer::ReadFileContent(const char *filename, Int_t &len)
1342{
1343 len = 0;
1344
1345 std::ifstream is(filename, std::ios::in | std::ios::binary);
1346 if (!is)
1347 return nullptr;
1348
1349 is.seekg(0, is.end);
1350 len = is.tellg();
1351 is.seekg(0, is.beg);
1352
1353 char *buf = (char *)malloc(len);
1354 is.read(buf, len);
1355 if (!is) {
1356 free(buf);
1357 len = 0;
1358 return nullptr;
1359 }
1360
1361 return buf;
1362}
1363
1364////////////////////////////////////////////////////////////////////////////////
1365/// reads file content, using std::string as container
1366
1367std::string THttpServer::ReadFileContent(const std::string &filename)
1368{
1369 std::ifstream is(filename, std::ios::in | std::ios::binary);
1370 std::string res;
1371 if (is) {
1372 is.seekg(0, std::ios::end);
1373 res.resize(is.tellg());
1374 is.seekg(0, std::ios::beg);
1375 is.read((char *)res.data(), res.length());
1376 if (!is)
1377 res.clear();
1378 }
1379 return res;
1380}
const Ssiz_t kNPOS
Definition RtypesCore.h:115
const Bool_t kFALSE
Definition RtypesCore.h:92
long Long_t
Definition RtypesCore.h:54
bool Bool_t
Definition RtypesCore.h:63
const Bool_t kTRUE
Definition RtypesCore.h:91
#define ClassImp(name)
Definition Rtypes.h:364
R__EXTERN TEnv * gEnv
Definition TEnv.h:171
char name[80]
Definition TGX11.cxx:110
#define gROOT
Definition TROOT.h:406
char * Form(const char *fmt,...)
R__EXTERN TSystem * gSystem
Definition TSystem.h:559
const char * mime_type
Definition civetweb.c:7795
size_t ext_len
Definition civetweb.c:7794
#define free
Definition civetweb.c:1539
const char * extension
Definition civetweb.c:7793
static const struct @138 builtin_mime_types[]
#define malloc
Definition civetweb.c:1536
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:80
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition TClass.cxx:4955
static TClass * LoadClass(const char *requestedname, Bool_t silent)
Helper function used by TClass::GetClass().
Definition TClass.cxx:5726
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:491
void Set404()
mark reply as 404 error - page/request not exists or refused
void SetServer(THttpServer *serv)
Definition THttpEngine.h:27
virtual Bool_t Create(const char *)
Method to create all components of engine.
Definition THttpEngine.h:37
virtual void Process()
Method regularly called in main ROOT context.
Definition THttpEngine.h:33
virtual void Terminate()
Method called when server want to be terminated.
Definition THttpEngine.h:30
Bool_t IsReadOnly() const
returns read-only mode
Bool_t RegisterCommand(const char *cmdname, const char *method, const char *icon=nullptr)
Register command which can be executed from web interface.
TString fJSROOT
! location of external JSROOT files
Definition THttpServer.h:45
virtual void ProcessRequest(std::shared_ptr< THttpCallArg > arg)
Process single http request Depending from requested path and filename different actions will be perf...
std::shared_ptr< THttpWSHandler > FindWS(const char *name)
Find web-socket handler with given name.
std::unique_ptr< TRootSniffer > fSniffer
! sniffer provides access to ROOT objects hierarchy
Definition THttpServer.h:36
void SetTimer(Long_t milliSec=100, Bool_t mode=kTRUE)
create timer which will invoke ProcessRequests() function periodically Timer is required to perform a...
virtual void ProcessBatchHolder(std::shared_ptr< THttpCallArg > &arg)
Process special http request for root_batch_holder.js script This kind of requests used to hold web b...
std::vector< std::shared_ptr< THttpWSHandler > > fWSHandlers
! list of WS handlers
Definition THttpServer.h:59
virtual ~THttpServer()
destructor delete all http engines and sniffer
void SetTerminate()
set termination flag, no any further requests will be processed
virtual void MissedRequest(THttpCallArg *arg)
Method called when THttpServer cannot process request By default such requests replied with 404 code ...
Bool_t fOwnThread
! true when specialized thread allocated for processing requests
Definition THttpServer.h:39
void SetSniffer(TRootSniffer *sniff)
Set TRootSniffer to the server Server takes ownership over sniffer.
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
void SetReadOnly(Bool_t readonly=kTRUE)
Set read-only mode for the server (default on) In read-only server is not allowed to change any ROOT ...
const char * GetItemField(const char *fullname, const char *name)
const char * GetCors() const
Returns specified CORS domain.
std::thread fThrd
! own thread
Definition THttpServer.h:40
void StopServerThread()
Stop server thread Normally called shortly before http server destructor.
Int_t ProcessRequests()
Process submitted requests, must be called from appropriate thread.
Bool_t ExecuteWS(std::shared_ptr< THttpCallArg > &arg, Bool_t external_thrd=kFALSE, Bool_t wait_process=kFALSE)
Execute WS request.
void RegisterWS(std::shared_ptr< THttpWSHandler > ws)
Register WS handler.
TString fTopName
! name of top folder, default - "ROOT"
Definition THttpServer.h:44
void SetDrawPage(const std::string &filename="")
Set file name of HTML page, delivered by the server when objects drawing page is requested from the b...
Bool_t CreateItem(const char *fullname, const char *title)
Bool_t ExecuteHttp(std::shared_ptr< THttpCallArg > arg)
Execute HTTP request.
Bool_t Hide(const char *fullname, Bool_t hide=kTRUE)
hides folder or element from web gui
void AddLocation(const char *prefix, const char *path)
add files location, which could be used in the server one could map some system folder to the server ...
std::map< std::string, std::string > fLocations
! list of local directories, which could be accessed via server
Definition THttpServer.h:47
Bool_t SubmitHttp(std::shared_ptr< THttpCallArg > arg, Bool_t can_run_immediately=kFALSE)
Submit HTTP request.
Long_t fMainThrdId
! id of the thread for processing requests
Definition THttpServer.h:38
TString fJSROOTSYS
! location of local JSROOT files
Definition THttpServer.h:43
std::unique_ptr< THttpTimer > fTimer
! timer used to access main thread
Definition THttpServer.h:35
Bool_t fWSOnly
! when true, handle only websockets / longpoll engine
Definition THttpServer.h:41
Bool_t Register(const char *subfolder, TObject *obj)
Register object in subfolder.
TList fEngines
! engines which runs http server
Definition THttpServer.h:34
void SetCors(const std::string &domain="*")
Enable CORS header to ProcessRequests() responses Specified location (typically "*") add as "Access-C...
Bool_t IsCors() const
Returns kTRUE if CORS was configured.
std::queue< std::shared_ptr< THttpCallArg > > fArgs
! submitted arguments
Definition THttpServer.h:56
void SetDefaultPage(const std::string &filename="")
Set file name of HTML page, delivered by the server when http address is opened in the browser.
THttpServer(const THttpServer &)=delete
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
void CreateServerThread()
Creates special thread to process all requests, directed to http server.
std::string fDrawPageCont
! content of draw html page
Definition THttpServer.h:52
Bool_t Unregister(TObject *obj)
Unregister object.
void SetWSOnly(Bool_t on=kTRUE)
Set websocket-only mode.
void ReplaceJSROOTLinks(std::shared_ptr< THttpCallArg > &arg)
Replaces all references like "jsrootsys/..." Either using pre-configured JSROOT installation from web...
std::string BuildWSEntryPage()
Create summary page with active WS handlers.
Bool_t IsWSOnly() const
returns true if only websockets are handled by the server Typically used by WebGui
std::mutex fWSMutex
! mutex to protect WS handler lists
Definition THttpServer.h:58
Bool_t CreateEngine(const char *engine)
factory method to create different http engines At the moment two engine kinds are supported: civetwe...
Bool_t SetIcon(const char *fullname, const char *iconname)
set name of icon, used in browser together with the item
std::string fDrawPage
! file name for drawing of single element
Definition THttpServer.h:51
std::string fDefaultPageCont
! content of default html page
Definition THttpServer.h:50
static Bool_t VerifyFilePath(const char *fname)
Checked that filename does not contains relative path below current directory Used to prevent access ...
Bool_t SetItemField(const char *fullname, const char *name, const char *value)
void SetJSROOT(const char *location)
Set location of JSROOT to use with the server One could specify address like: https://root....
std::mutex fMutex
! mutex to protect list with arguments
Definition THttpServer.h:55
std::string fDefaultPage
! file name for default page name
Definition THttpServer.h:49
void UnregisterWS(std::shared_ptr< THttpWSHandler > ws)
Unregister WS handler.
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
TRootSniffer * GetSniffer() const
returns pointer on objects sniffer
Definition THttpServer.h:87
Bool_t fTerminated
! termination flag, disables all requests processing
Definition THttpServer.h:37
void Restrict(const char *path, const char *options)
Restrict access to specified object.
virtual TString GetDefaultPageContent()
Provides content of default web page for registered web-socket handler Can be content of HTML page or...
virtual void Add(TObject *obj)
Definition TList.h:87
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
Definition TList.cxx:470
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
An array of TObjects.
Definition TObjArray.h:37
Int_t GetLast() const
Return index of last object in array.
TObject * At(Int_t idx) const
Definition TObjArray.h:166
Mother of all ROOT objects.
Definition TObject.h:37
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:359
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:879
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:893
static const TString & GetRootSys()
Get the rootsys directory in the installation. Static utility function.
Definition TROOT.cxx:2907
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:2979
Storage of hierarchy scan in TRootSniffer in JSON format.
Storage of hierarchy scan in TRootSniffer in XML format.
void CreateOwnTopFolder()
Create own TFolder structures independent from gROOT This allows to have many independent TRootSniffe...
void SetReadOnly(Bool_t on=kTRUE)
When readonly on (default), sniffer is not allowed to change ROOT structures For instance,...
void SetScanGlobalDir(Bool_t on=kTRUE)
When enabled (default), sniffer scans gROOT for files, canvases, histograms.
Basic string class.
Definition TString.h:136
Ssiz_t Length() const
Definition TString.h:410
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2197
Ssiz_t First(char c) const
Find first occurrence of a character c.
Definition TString.cxx:519
const char * Data() const
Definition TString.h:369
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Definition TString.cxx:1115
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition TString.cxx:912
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition TString.cxx:2217
TString & Remove(Ssiz_t pos)
Definition TString.h:673
TString & Append(const char *cs)
Definition TString.h:564
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2331
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2309
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:639
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1272
virtual const char * Getenv(const char *env)
Get environment variable.
Definition TSystem.cxx:1661
static Long_t SelfId()
Static method returning the id for the current thread.
Definition TThread.cxx:554
Handles synchronous and a-synchronous timer events.
Definition TTimer.h:51
virtual void Timeout()
Definition TTimer.h:96
This class represents a WWW compatible URL.
Definition TUrl.h:33
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
Definition TUrl.cxx:659
void SetOptions(const char *opt)
Definition TUrl.h:87
void ParseOptions() const
Parse URL options into a key/value map.
Definition TUrl.cxx:625
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
Definition TUrl.cxx:682
const Int_t n
Definition legend1.C:16
void ws()
Definition ws.C:66