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