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
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 }
831
832 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() && IsWSOnly()) {
833 arg->fContent = BuildWSEntryPage();
834 }
835
836 if (arg->fContent.empty() && !IsWSOnly()) {
837
838 if (fDefaultPageCont.empty())
840
841 arg->fContent = fDefaultPageCont;
842 }
843
844 if (arg->fContent.empty()) {
845
846 arg->Set404();
847 } else if (!arg->Is404()) {
848
850
851 const char *hjsontag = "\"$$$h.json$$$\"";
852
853 // add h.json caching
854 if (arg->fContent.find(hjsontag) != std::string::npos) {
855 TString h_json;
856 TRootSnifferStoreJson store(h_json, kTRUE);
857 const char *topname = fTopName.Data();
858 if (arg->fTopName.Length() > 0)
859 topname = arg->fTopName.Data();
860 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
861
862 arg->ReplaceAllinContent(hjsontag, h_json.Data());
863
864 arg->AddNoCacheHeader();
865
866 if (arg->fQuery.Index("nozip") == kNPOS)
867 arg->SetZipping();
868 }
869 arg->SetContentType("text/html");
870 }
871 return;
872 }
873
874 if ((arg->fFileName == "draw.htm") && !IsWSOnly()) {
875 if (fDrawPageCont.empty())
877
878 if (fDrawPageCont.empty()) {
879 arg->Set404();
880 } else {
881 const char *rootjsontag = "\"$$$root.json$$$\"";
882 const char *hjsontag = "\"$$$h.json$$$\"";
883
884 arg->fContent = fDrawPageCont;
885
887
888 if ((arg->fQuery.Index("no_h_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
889 (arg->fContent.find(hjsontag) != std::string::npos)) {
890 TString h_json;
891 TRootSnifferStoreJson store(h_json, kTRUE);
892 const char *topname = fTopName.Data();
893 if (arg->fTopName.Length() > 0)
894 topname = arg->fTopName.Data();
895 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
896
897 arg->ReplaceAllinContent(hjsontag, h_json.Data());
898 }
899
900 if ((arg->fQuery.Index("no_root_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
901 (arg->fContent.find(rootjsontag) != std::string::npos)) {
902 std::string str;
903 if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=23", str))
904 arg->ReplaceAllinContent(rootjsontag, str);
905 }
906 arg->AddNoCacheHeader();
907 if (arg->fQuery.Index("nozip") == kNPOS)
908 arg->SetZipping();
909 arg->SetContentType("text/html");
910 }
911 return;
912 }
913
914 if ((arg->fFileName == "favicon.ico") && arg->fPathName.IsNull()) {
915 arg->SetFile(fJSROOTSYS + "/img/RootIcon.ico");
916 return;
917 }
918
919 TString filename;
920 if (IsFileRequested(arg->fFileName.Data(), filename)) {
921 arg->SetFile(filename);
922 return;
923 }
924
925 // check if websocket handler may serve file request
926 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
927 TString wsname = arg->fPathName, fname;
928 auto pos = wsname.First('/');
929 if (pos == kNPOS) {
930 wsname = arg->fPathName;
931 } else {
932 wsname = arg->fPathName(0, pos);
933 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
934 fname.Append("/");
935 }
936
937 fname.Append(arg->fFileName);
938
939 if (VerifyFilePath(fname.Data())) {
940
941 auto ws = FindWS(wsname.Data());
942
943 if (ws && ws->CanServeFiles()) {
944 TString fdir = ws->GetDefaultPageContent();
945 // only when file is specified, can take directory, append prefix and file name
946 if (fdir.Index("file:") == 0) {
947 fdir.Remove(0, 5);
948 auto separ = fdir.Last('/');
949 if (separ != kNPOS)
950 fdir.Resize(separ + 1);
951 else
952 fdir = "./";
953
954 fdir.Append(fname);
955 arg->SetFile(fdir);
956 return;
957 }
958 }
959 }
960 }
961
962 filename = arg->fFileName;
963
964 Bool_t iszip = kFALSE;
965 if (filename.EndsWith(".gz")) {
966 filename.Resize(filename.Length() - 3);
967 iszip = kTRUE;
968 }
969
970 if (IsWSOnly()) {
971 if (arg->fContent.empty())
972 arg->Set404();
973 } else if ((filename == "h.xml") || (filename == "get.xml")) {
974
975 Bool_t compact = arg->fQuery.Index("compact") != kNPOS;
976
977 TString res;
978
979 res.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
980 if (!compact)
981 res.Append("\n");
982 res.Append("<root>");
983 if (!compact)
984 res.Append("\n");
985 {
986 TRootSnifferStoreXml store(res, compact);
987
988 const char *topname = fTopName.Data();
989 if (arg->fTopName.Length() > 0)
990 topname = arg->fTopName.Data();
991 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
992 }
993
994 res.Append("</root>");
995 if (!compact)
996 res.Append("\n");
997
998 arg->SetContent(std::string(res.Data()));
999
1000 arg->SetXml();
1001 } else if (filename == "h.json") {
1002 TString res;
1003 TRootSnifferStoreJson store(res, arg->fQuery.Index("compact") != kNPOS);
1004 const char *topname = fTopName.Data();
1005 if (arg->fTopName.Length() > 0)
1006 topname = arg->fTopName.Data();
1007 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1008 arg->SetContent(std::string(res.Data()));
1009 arg->SetJson();
1010 } else if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), arg->fContent)) {
1011 // define content type base on extension
1012 arg->SetContentType(GetMimeType(filename.Data()));
1013 } else {
1014 // miss request, user may process
1015 MissedRequest(arg.get());
1016 }
1017
1018 if (arg->Is404())
1019 return;
1020
1021 if (iszip)
1022 arg->SetZipping(THttpCallArg::kZipAlways);
1023
1024 if (filename == "root.bin") {
1025 // only for binary data master version is important
1026 // it allows to detect if streamer info was modified
1027 const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
1028 arg->AddHeader(parname, Form("%u", (unsigned)fSniffer->GetStreamerInfoHash()));
1029 }
1030
1031 // try to avoid caching on the browser
1032 arg->AddNoCacheHeader();
1033
1034 // potentially add cors header
1035 if (IsCors())
1036 arg->AddHeader("Access-Control-Allow-Origin", GetCors());
1037}
1038
1039////////////////////////////////////////////////////////////////////////////////
1040/// Register object in folders hierarchy
1041///
1042/// See TRootSniffer::RegisterObject() for more details
1043
1044Bool_t THttpServer::Register(const char *subfolder, TObject *obj)
1045{
1046 return fSniffer->RegisterObject(subfolder, obj);
1047}
1048
1049////////////////////////////////////////////////////////////////////////////////
1050/// Unregister object in folders hierarchy
1051///
1052/// See TRootSniffer::UnregisterObject() for more details
1053
1055{
1056 return fSniffer->UnregisterObject(obj);
1057}
1058
1059////////////////////////////////////////////////////////////////////////////////
1060/// Register WS handler to the THttpServer
1061///
1062/// Only such handler can be used in multi-threaded processing of websockets
1063
1064void THttpServer::RegisterWS(std::shared_ptr<THttpWSHandler> ws)
1065{
1066 std::lock_guard<std::mutex> grd(fWSMutex);
1067 fWSHandlers.emplace_back(ws);
1068}
1069
1070////////////////////////////////////////////////////////////////////////////////
1071/// Unregister WS handler to the THttpServer
1072
1073void THttpServer::UnregisterWS(std::shared_ptr<THttpWSHandler> ws)
1074{
1075 std::lock_guard<std::mutex> grd(fWSMutex);
1076 for (int n = (int)fWSHandlers.size(); n > 0; --n)
1077 if ((fWSHandlers[n - 1] == ws) || fWSHandlers[n - 1]->IsDisabled())
1078 fWSHandlers.erase(fWSHandlers.begin() + n - 1);
1079}
1080
1081////////////////////////////////////////////////////////////////////////////////
1082/// Search WS handler with given name
1083///
1084/// Handler must be registered with RegisterWS() method
1085
1086std::shared_ptr<THttpWSHandler> THttpServer::FindWS(const char *name)
1087{
1088 std::lock_guard<std::mutex> grd(fWSMutex);
1089 for (auto &ws : fWSHandlers) {
1090 if (strcmp(name, ws->GetName()) == 0)
1091 return ws;
1092 }
1093
1094 return nullptr;
1095}
1096
1097////////////////////////////////////////////////////////////////////////////////
1098/// Execute WS related operation
1099
1100Bool_t THttpServer::ExecuteWS(std::shared_ptr<THttpCallArg> &arg, Bool_t external_thrd, Bool_t wait_process)
1101{
1102 if (fTerminated) {
1103 arg->Set404();
1104 return kFALSE;
1105 }
1106
1107 auto wsptr = FindWS(arg->GetPathName());
1108
1109 auto handler = wsptr.get();
1110
1111 if (!handler && !external_thrd)
1112 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1113
1114 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1115 std::unique_lock<std::mutex> lk(fMutex);
1116 fArgs.push(arg);
1117 // and now wait until request is processed
1118 if (wait_process)
1119 arg->fCond.wait(lk);
1120
1121 return kTRUE;
1122 }
1123
1124 if (!handler)
1125 return kFALSE;
1126
1127 Bool_t process = kFALSE;
1128
1129 if (arg->fFileName == "root.websocket") {
1130 // handling of web socket
1131 process = handler->HandleWS(arg);
1132 } else if (arg->fFileName == "root.longpoll") {
1133 // ROOT emulation of websocket with polling requests
1134 if (arg->fQuery.BeginsWith("raw_connect") || arg->fQuery.BeginsWith("txt_connect")) {
1135 // try to emulate websocket connect
1136 // if accepted, reply with connection id, which must be used in the following communications
1137 arg->SetMethod("WS_CONNECT");
1138
1139 bool israw = arg->fQuery.BeginsWith("raw_connect");
1140
1141 // automatically assign engine to arg
1142 arg->CreateWSEngine<THttpLongPollEngine>(israw);
1143
1144 if (handler->HandleWS(arg)) {
1145 arg->SetMethod("WS_READY");
1146
1147 if (handler->HandleWS(arg))
1148 arg->SetTextContent(std::string(israw ? "txt:" : "") + std::to_string(arg->GetWSId()));
1149 } else {
1150 arg->TakeWSEngine(); // delete handle
1151 }
1152
1153 process = arg->IsText();
1154 } else {
1155 TUrl url;
1156 url.SetOptions(arg->fQuery);
1157 url.ParseOptions();
1158 Int_t connid = url.GetIntValueFromOptions("connection");
1159 arg->SetWSId((UInt_t)connid);
1160 if (url.HasOption("close")) {
1161 arg->SetMethod("WS_CLOSE");
1162 arg->SetTextContent("OK");
1163 } else {
1164 arg->SetMethod("WS_DATA");
1165 }
1166
1167 process = handler->HandleWS(arg);
1168 }
1169 }
1170
1171 if (!process)
1172 arg->Set404();
1173
1174 return process;
1175}
1176
1177////////////////////////////////////////////////////////////////////////////////
1178/// Restrict access to specified object
1179///
1180/// See TRootSniffer::Restrict() for more details
1181
1182void THttpServer::Restrict(const char *path, const char *options)
1183{
1184 fSniffer->Restrict(path, options);
1185}
1186
1187////////////////////////////////////////////////////////////////////////////////
1188/// Register command which can be executed from web interface
1189///
1190/// As method one typically specifies string, which is executed with
1191/// gROOT->ProcessLine() method. For instance
1192/// serv->RegisterCommand("Invoke","InvokeFunction()");
1193///
1194/// Or one could specify any method of the object which is already registered
1195/// to the server. For instance:
1196/// serv->Register("/", hpx);
1197/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1198/// Here symbols '/->' separates item name from method to be executed
1199///
1200/// One could specify additional arguments in the command with
1201/// syntax like %arg1%, %arg2% and so on. For example:
1202/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1203/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1204/// Such parameter(s) will be requested when command clicked in the browser.
1205///
1206/// Once command is registered, one could specify icon which will appear in the browser:
1207/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1208///
1209/// One also can set extra property '_fastcmd', that command appear as
1210/// tool button on the top of the browser tree:
1211/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1212/// Or it is equivalent to specifying extra argument when register command:
1213/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1214
1215Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1216{
1217 return fSniffer->RegisterCommand(cmdname, method, icon);
1218}
1219
1220////////////////////////////////////////////////////////////////////////////////
1221/// hides folder or element from web gui
1222
1223Bool_t THttpServer::Hide(const char *foldername, Bool_t hide)
1224{
1225 return SetItemField(foldername, "_hidden", hide ? "true" : (const char *)0);
1226}
1227
1228////////////////////////////////////////////////////////////////////////////////
1229/// set name of icon, used in browser together with the item
1230///
1231/// One could use images from $ROOTSYS directory like:
1232/// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
1233
1234Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
1235{
1236 return SetItemField(fullname, "_icon", iconname);
1237}
1238
1239////////////////////////////////////////////////////////////////////////////////
1240
1241Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
1242{
1243 return fSniffer->CreateItem(fullname, title);
1244}
1245
1246////////////////////////////////////////////////////////////////////////////////
1247
1248Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
1249{
1250 return fSniffer->SetItemField(fullname, name, value);
1251}
1252
1253////////////////////////////////////////////////////////////////////////////////
1254
1255const char *THttpServer::GetItemField(const char *fullname, const char *name)
1256{
1257 return fSniffer->GetItemField(fullname, name);
1258}
1259
1260////////////////////////////////////////////////////////////////////////////////
1261/// Returns MIME type base on file extension
1262
1263const char *THttpServer::GetMimeType(const char *path)
1264{
1265 static const struct {
1266 const char *extension;
1267 int ext_len;
1268 const char *mime_type;
1269 } builtin_mime_types[] = {{".xml", 4, "text/xml"},
1270 {".json", 5, "application/json"},
1271 {".bin", 4, "application/x-binary"},
1272 {".gif", 4, "image/gif"},
1273 {".jpg", 4, "image/jpeg"},
1274 {".png", 4, "image/png"},
1275 {".html", 5, "text/html"},
1276 {".htm", 4, "text/html"},
1277 {".shtm", 5, "text/html"},
1278 {".shtml", 6, "text/html"},
1279 {".css", 4, "text/css"},
1280 {".js", 3, "application/x-javascript"},
1281 {".ico", 4, "image/x-icon"},
1282 {".jpeg", 5, "image/jpeg"},
1283 {".svg", 4, "image/svg+xml"},
1284 {".txt", 4, "text/plain"},
1285 {".torrent", 8, "application/x-bittorrent"},
1286 {".wav", 4, "audio/x-wav"},
1287 {".mp3", 4, "audio/x-mp3"},
1288 {".mid", 4, "audio/mid"},
1289 {".m3u", 4, "audio/x-mpegurl"},
1290 {".ogg", 4, "application/ogg"},
1291 {".ram", 4, "audio/x-pn-realaudio"},
1292 {".xslt", 5, "application/xml"},
1293 {".xsl", 4, "application/xml"},
1294 {".ra", 3, "audio/x-pn-realaudio"},
1295 {".doc", 4, "application/msword"},
1296 {".exe", 4, "application/octet-stream"},
1297 {".zip", 4, "application/x-zip-compressed"},
1298 {".xls", 4, "application/excel"},
1299 {".tgz", 4, "application/x-tar-gz"},
1300 {".tar", 4, "application/x-tar"},
1301 {".gz", 3, "application/x-gunzip"},
1302 {".arj", 4, "application/x-arj-compressed"},
1303 {".rar", 4, "application/x-arj-compressed"},
1304 {".rtf", 4, "application/rtf"},
1305 {".pdf", 4, "application/pdf"},
1306 {".swf", 4, "application/x-shockwave-flash"},
1307 {".mpg", 4, "video/mpeg"},
1308 {".webm", 5, "video/webm"},
1309 {".mpeg", 5, "video/mpeg"},
1310 {".mov", 4, "video/quicktime"},
1311 {".mp4", 4, "video/mp4"},
1312 {".m4v", 4, "video/x-m4v"},
1313 {".asf", 4, "video/x-ms-asf"},
1314 {".avi", 4, "video/x-msvideo"},
1315 {".bmp", 4, "image/bmp"},
1316 {".ttf", 4, "application/x-font-ttf"},
1317 {".woff", 5, "font/woff"},
1318 {".woff2", 6, "font/woff2"},
1319 {NULL, 0, NULL}};
1320
1321 int path_len = strlen(path);
1322
1323 for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
1324 if (path_len <= builtin_mime_types[i].ext_len)
1325 continue;
1326 const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
1327 if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
1328 return builtin_mime_types[i].mime_type;
1329 }
1330 }
1331
1332 return "text/plain";
1333}
1334
1335////////////////////////////////////////////////////////////////////////////////
1336/// \deprecated reads file content
1337
1338char *THttpServer::ReadFileContent(const char *filename, Int_t &len)
1339{
1340 len = 0;
1341
1342 std::ifstream is(filename, std::ios::in | std::ios::binary);
1343 if (!is)
1344 return nullptr;
1345
1346 is.seekg(0, is.end);
1347 len = is.tellg();
1348 is.seekg(0, is.beg);
1349
1350 char *buf = (char *)malloc(len);
1351 is.read(buf, len);
1352 if (!is) {
1353 free(buf);
1354 len = 0;
1355 return nullptr;
1356 }
1357
1358 return buf;
1359}
1360
1361////////////////////////////////////////////////////////////////////////////////
1362/// reads file content, using std::string as container
1363
1364std::string THttpServer::ReadFileContent(const std::string &filename)
1365{
1366 std::ifstream is(filename, std::ios::in | std::ios::binary);
1367 std::string res;
1368 if (is) {
1369 is.seekg(0, std::ios::end);
1370 res.resize(is.tellg());
1371 is.seekg(0, std::ios::beg);
1372 is.read((char *)res.data(), res.length());
1373 if (!is)
1374 res.clear();
1375 }
1376 return res;
1377}
const Ssiz_t kNPOS
Definition: RtypesCore.h:124
int Int_t
Definition: RtypesCore.h:45
int Ssiz_t
Definition: RtypesCore.h:67
unsigned int UInt_t
Definition: RtypesCore.h:46
const Bool_t kFALSE
Definition: RtypesCore.h:101
long Long_t
Definition: RtypesCore.h:54
bool Bool_t
Definition: RtypesCore.h:63
const Bool_t kTRUE
Definition: RtypesCore.h:100
#define ClassImp(name)
Definition: Rtypes.h:364
R__EXTERN TEnv * gEnv
Definition: TEnv.h:170
char name[80]
Definition: TGX11.cxx:110
#define gROOT
Definition: TROOT.h:404
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
#define malloc
Definition: civetweb.c:1536
static const struct @144 builtin_mime_types[]
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:4971
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
Definition: THttpCallArg.h:163
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.
Definition: THttpServer.h:113
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...
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 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.
void Timeout() override
timeout handler used to process http requests in main ROOT thread
Definition: THttpServer.cxx:61
THttpServer & fServer
Definition: THttpServer.cxx:54
THttpTimer(Long_t milliSec, Bool_t mode, THttpServer &serv)
!< server processing requests
Definition: THttpServer.cxx:57
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.
Definition: TObjArray.cxx:577
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:2891
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition: TROOT.cxx:2963
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,...
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
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition: TString.cxx:2202
Ssiz_t First(char c) const
Find first occurrence of a character c.
Definition: TString.cxx:523
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:1120
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:916
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition: TString.cxx:2222
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:2336
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2314
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:1274
virtual const char * Getenv(const char *env)
Get environment variable.
Definition: TSystem.cxx:1663
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
Int_t GetIntValueFromOptions(const char *key) const
Return a value for a given key from the URL options as an Int_t, a missing key returns -1.
Definition: TUrl.cxx:671
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
const char * cnt
Definition: TXMLSetup.cxx:75
void ws()
Definition: ws.C:66