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