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.6.0/
347/// * https://jsroot.gsi.de/7.6.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 // add call arg to the list
691 std::unique_lock<std::mutex> lk(fMutex);
692 fArgs.push(arg);
693 return kFALSE;
694}
695
696////////////////////////////////////////////////////////////////////////////////
697/// Process requests, submitted for execution
698///
699/// Returns number of processed requests
700///
701/// Normally invoked by THttpTimer, when somewhere in the code
702/// gSystem->ProcessEvents() is called.
703/// User can call serv->ProcessRequests() directly, but only from main thread.
704/// If special server thread is created, called from that thread
705
707{
708 auto id = TThread::SelfId();
709
710 if (fMainThrdId != id) {
711 if (gDebug > 0 && fMainThrdId)
712 Warning("ProcessRequests", "Changing main thread to %ld", (long)id);
713 fMainThrdId = id;
714 }
715
716 Bool_t recursion = kFALSE;
717
718 if (fProcessingThrdId) {
719 if (fProcessingThrdId == id) {
720 recursion = kTRUE;
721 } else {
722 Error("ProcessRequests", "Processing already running from %ld thread", (long) fProcessingThrdId);
723 return 0;
724 }
725 }
726
727 if (!recursion)
729
730 Int_t cnt = 0;
731
732 std::unique_lock<std::mutex> lk(fMutex, std::defer_lock);
733
734 // first process requests in the queue
735 while (true) {
736 std::shared_ptr<THttpCallArg> arg;
737
738 lk.lock();
739 if (!fArgs.empty()) {
740 arg = fArgs.front();
741 fArgs.pop();
742 }
743 lk.unlock();
744
745 if (!arg)
746 break;
747
748 if (arg->fFileName == "root_batch_holder.js") {
750 continue;
751 }
752
753 auto prev = fSniffer->SetCurrentCallArg(arg.get());
754
755 try {
756 cnt++;
757 ProcessRequest(arg);
758 fSniffer->SetCurrentCallArg(prev);
759 } catch (...) {
760 fSniffer->SetCurrentCallArg(prev);
761 }
762
763 arg->NotifyCondition();
764 }
765
766 // regularly call Process() method of engine to let perform actions in ROOT context
767 TIter iter(&fEngines);
768 while (auto engine = static_cast<THttpEngine *>(iter())) {
769 if (fTerminated)
770 engine->Terminate();
771 engine->Process();
772 }
773
774 if (!recursion)
776
777 return cnt;
778}
779
780////////////////////////////////////////////////////////////////////////////////
781/// Method called when THttpServer cannot process request
782///
783/// By default such requests replied with 404 code
784/// One could overwrite with method in derived class to process all kinds of such non-standard requests
785
787{
788 arg->Set404();
789}
790
791////////////////////////////////////////////////////////////////////////////////
792/// Process special http request for root_batch_holder.js script
793///
794/// This kind of requests used to hold web browser running in headless mode
795/// Intentionally requests does not replied immediately
796
797void THttpServer::ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg)
798{
799 auto wsptr = FindWS(arg->GetPathName());
800
801 if (!wsptr || !wsptr->ProcessBatchHolder(arg)) {
802 arg->Set404();
803 arg->NotifyCondition();
804 }
805}
806
807////////////////////////////////////////////////////////////////////////////////
808/// Create summary page with active WS handlers
809
811{
812
813 std::string arr = "[";
814
815 {
816 std::lock_guard<std::mutex> grd(fWSMutex);
817 for (auto &ws : fWSHandlers) {
818 if (arr.length() > 1)
819 arr.append(", ");
820
821 arr.append(TString::Format("{ name: \"%s\", title: \"%s\" }", ws->GetName(), ws->GetTitle()).Data());
822 }
823 }
824
825 arr.append("]");
826
827 std::string res = ReadFileContent((TROOT::GetDataDir() + "/js/files/wslist.htm").Data());
828
829 std::string arg = "\"$$$wslist$$$\"";
830
831 auto pos = res.find(arg);
832 if (pos != std::string::npos)
833 res.replace(pos, arg.length(), arr);
834
835 return res;
836}
837
838////////////////////////////////////////////////////////////////////////////////
839/// Replaces all references like "jsrootsys/..."
840///
841/// Either using pre-configured JSROOT installation from web or
842/// redirect to jsrootsys from the main server path to benefit from browser caching
843
844void THttpServer::ReplaceJSROOTLinks(std::shared_ptr<THttpCallArg> &arg)
845{
846 std::string repl;
847
848 if (fJSROOT.Length() > 0) {
849 repl = "=\"";
850 repl.append(fJSROOT.Data());
851 if (repl.back() != '/')
852 repl.append("/");
853 } else {
854 Int_t cnt = 0;
855 if (arg->fPathName.Length() > 0) cnt++;
856 for (Int_t n = 1; n < arg->fPathName.Length()-1; ++n)
857 if (arg->fPathName[n] == '/') {
858 if (arg->fPathName[n-1] != '/') {
859 cnt++; // normal slash in the middle, count it
860 } else {
861 cnt = 0; // double slash, do not touch such path
862 break;
863 }
864 }
865
866 if (cnt > 0) {
867 repl = "=\"";
868 while (cnt-- >0) repl.append("../");
869 repl.append("jsrootsys/");
870 }
871 }
872
873 if (!repl.empty()) {
874 arg->ReplaceAllinContent("=\"jsrootsys/", repl);
875 arg->ReplaceAllinContent("from './jsrootsys/", TString::Format("from '%s", repl.substr(2).c_str()).Data());
876 }
877}
878
879////////////////////////////////////////////////////////////////////////////////
880/// Process single http request
881///
882/// Depending from requested path and filename different actions will be performed.
883/// In most cases information is provided by TRootSniffer class
884
885void THttpServer::ProcessRequest(std::shared_ptr<THttpCallArg> arg)
886{
887 if (fTerminated) {
888 arg->Set404();
889 return;
890 }
891
892 if ((arg->fFileName == "root.websocket") || (arg->fFileName == "root.longpoll")) {
893 ExecuteWS(arg);
894 return;
895 }
896
897 if (arg->fFileName.IsNull() || (arg->fFileName == "index.htm") || (arg->fFileName == "default.htm")) {
898
899 if (arg->fFileName == "default.htm") {
900
901 if (!IsWSOnly())
902 arg->fContent = ReadFileContent((fJSROOTSYS + "/files/online.htm").Data());
903
904 } else {
905 auto wsptr = FindWS(arg->GetPathName());
906
907 auto handler = wsptr.get();
908
909 if (!handler)
910 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
911
912 if (handler) {
913
914 arg->fContent = handler->GetDefaultPageContent().Data();
915
916 if (arg->fContent.find("file:") == 0) {
917 const char *fname = arg->fContent.c_str() + 5;
918 TString resolve;
919 if (!IsFileRequested(fname, resolve)) resolve = fname;
920 arg->fContent = ReadFileContent(resolve.Data());
921 }
922
923 handler->VerifyDefaultPageContent(arg);
924 }
925 }
926
927 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() && IsWSOnly()) {
928 // Creating page with list of available widgets is disabled now for security reasons
929 // Later one can provide functionality back only if explicitely desired by the user
930 // BuildWSEntryPage();
931
932 arg->SetContent("refused");
933 arg->Set404();
934 }
935
936 if (arg->fContent.empty() && !IsWSOnly()) {
937
938 if (fDefaultPageCont.empty())
940
941 arg->fContent = fDefaultPageCont;
942 }
943
944 if (arg->fContent.empty()) {
945
946 arg->Set404();
947 } else if (!arg->Is404()) {
948
950
951 const char *hjsontag = "\"$$$h.json$$$\"";
952
953 // add h.json caching
954 if (arg->fContent.find(hjsontag) != std::string::npos) {
955 TString h_json;
956 TRootSnifferStoreJson store(h_json, kTRUE);
957 const char *topname = fTopName.Data();
958 if (arg->fTopName.Length() > 0)
959 topname = arg->fTopName.Data();
960 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
961
962 arg->ReplaceAllinContent(hjsontag, h_json.Data());
963
964 arg->AddNoCacheHeader();
965
966 if (arg->fQuery.Index("nozip") == kNPOS)
967 arg->SetZipping();
968 }
969 arg->SetContentType("text/html");
970 }
971 return;
972 }
973
974 if ((arg->fFileName == "draw.htm") && !IsWSOnly()) {
975 if (fDrawPageCont.empty())
977
978 if (fDrawPageCont.empty()) {
979 arg->Set404();
980 } else {
981 const char *rootjsontag = "\"$$$root.json$$$\"";
982 const char *hjsontag = "\"$$$h.json$$$\"";
983
984 arg->fContent = fDrawPageCont;
985
987
988 if ((arg->fQuery.Index("no_h_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
989 (arg->fContent.find(hjsontag) != std::string::npos)) {
990 TString h_json;
991 TRootSnifferStoreJson store(h_json, kTRUE);
992 const char *topname = fTopName.Data();
993 if (arg->fTopName.Length() > 0)
994 topname = arg->fTopName.Data();
995 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
996
997 arg->ReplaceAllinContent(hjsontag, h_json.Data());
998 }
999
1000 if ((arg->fQuery.Index("no_root_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
1001 (arg->fContent.find(rootjsontag) != std::string::npos)) {
1002 std::string str;
1003 if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=23", str))
1004 arg->ReplaceAllinContent(rootjsontag, str);
1005 }
1006 arg->AddNoCacheHeader();
1007 if (arg->fQuery.Index("nozip") == kNPOS)
1008 arg->SetZipping();
1009 arg->SetContentType("text/html");
1010 }
1011 return;
1012 }
1013
1014 if ((arg->fFileName == "favicon.ico") && arg->fPathName.IsNull()) {
1015 arg->SetFile(fJSROOTSYS + "/img/RootIcon.ico");
1016 return;
1017 }
1018
1020 if (IsFileRequested(arg->fFileName.Data(), filename)) {
1021 arg->SetFile(filename);
1022 return;
1023 }
1024
1025 // check if websocket handler may serve file request
1026 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
1027 TString wsname = arg->fPathName, fname;
1028 auto pos = wsname.First('/');
1029 if (pos == kNPOS) {
1030 wsname = arg->fPathName;
1031 } else {
1032 wsname = arg->fPathName(0, pos);
1033 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
1034 fname.Append("/");
1035 }
1036
1037 fname.Append(arg->fFileName);
1038
1039 if (VerifyFilePath(fname.Data())) {
1040
1041 auto ws = FindWS(wsname.Data());
1042
1043 if (ws && ws->CanServeFiles()) {
1044 TString fdir = ws->GetDefaultPageContent();
1045 // only when file is specified, can take directory, append prefix and file name
1046 if (fdir.Index("file:") == 0) {
1047 fdir.Remove(0, 5);
1048 auto separ = fdir.Last('/');
1049 if (separ != kNPOS)
1050 fdir.Resize(separ + 1);
1051 else
1052 fdir = "./";
1053
1054 fdir.Append(fname);
1055 arg->SetFile(fdir);
1056 return;
1057 }
1058 }
1059 }
1060 }
1061
1062 filename = arg->fFileName;
1063
1064 Bool_t iszip = kFALSE;
1065 if (filename.EndsWith(".gz")) {
1066 filename.Resize(filename.Length() - 3);
1067 iszip = kTRUE;
1068 }
1069
1070 if (IsWSOnly()) {
1071 if (arg->fContent.empty())
1072 arg->Set404();
1073 } else if ((filename == "h.xml") || (filename == "get.xml")) {
1074
1075 Bool_t compact = arg->fQuery.Index("compact") != kNPOS;
1076
1077 TString res;
1078
1079 res.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1080 if (!compact)
1081 res.Append("\n");
1082 res.Append("<root>");
1083 if (!compact)
1084 res.Append("\n");
1085 {
1086 TRootSnifferStoreXml store(res, compact);
1087
1088 const char *topname = fTopName.Data();
1089 if (arg->fTopName.Length() > 0)
1090 topname = arg->fTopName.Data();
1091 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
1092 }
1093
1094 res.Append("</root>");
1095 if (!compact)
1096 res.Append("\n");
1097
1098 arg->SetContent(std::string(res.Data()));
1099
1100 arg->SetXml();
1101 } else if (filename == "h.json") {
1102 TString res;
1103 TRootSnifferStoreJson store(res, arg->fQuery.Index("compact") != kNPOS);
1104 const char *topname = fTopName.Data();
1105 if (arg->fTopName.Length() > 0)
1106 topname = arg->fTopName.Data();
1107 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1108 arg->SetContent(std::string(res.Data()));
1109 arg->SetJson();
1110 } else if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), arg->fContent)) {
1111 // define content type base on extension
1112 arg->SetContentType(GetMimeType(filename.Data()));
1113 } else {
1114 // miss request, user may process
1115 MissedRequest(arg.get());
1116 }
1117
1118 if (arg->Is404())
1119 return;
1120
1121 if (iszip)
1122 arg->SetZipping(THttpCallArg::kZipAlways);
1123
1124 if (filename == "root.bin") {
1125 // only for binary data master version is important
1126 // it allows to detect if streamer info was modified
1127 const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
1128 arg->AddHeader(parname, TString::Format("%u", (unsigned)fSniffer->GetStreamerInfoHash()).Data());
1129 }
1130
1131 // try to avoid caching on the browser
1132 arg->AddNoCacheHeader();
1133
1134 // potentially add cors headers
1135 if (IsCors())
1136 arg->AddHeader("Access-Control-Allow-Origin", GetCors());
1137 if (IsCorsCredentials())
1138 arg->AddHeader("Access-Control-Allow-Credentials", GetCorsCredentials());
1139}
1140
1141////////////////////////////////////////////////////////////////////////////////
1142/// Register object in folders hierarchy
1143///
1144/// See TRootSniffer::RegisterObject() for more details
1145
1146Bool_t THttpServer::Register(const char *subfolder, TObject *obj)
1147{
1148 return fSniffer->RegisterObject(subfolder, obj);
1149}
1150
1151////////////////////////////////////////////////////////////////////////////////
1152/// Unregister object in folders hierarchy
1153///
1154/// See TRootSniffer::UnregisterObject() for more details
1155
1157{
1158 return fSniffer->UnregisterObject(obj);
1159}
1160
1161////////////////////////////////////////////////////////////////////////////////
1162/// Register WS handler to the THttpServer
1163///
1164/// Only such handler can be used in multi-threaded processing of websockets
1165
1166void THttpServer::RegisterWS(std::shared_ptr<THttpWSHandler> ws)
1167{
1168 std::lock_guard<std::mutex> grd(fWSMutex);
1169 fWSHandlers.emplace_back(ws);
1170}
1171
1172////////////////////////////////////////////////////////////////////////////////
1173/// Unregister WS handler to the THttpServer
1174
1175void THttpServer::UnregisterWS(std::shared_ptr<THttpWSHandler> ws)
1176{
1177 std::lock_guard<std::mutex> grd(fWSMutex);
1178 for (int n = (int)fWSHandlers.size(); n > 0; --n)
1179 if ((fWSHandlers[n - 1] == ws) || fWSHandlers[n - 1]->IsDisabled())
1180 fWSHandlers.erase(fWSHandlers.begin() + n - 1);
1181}
1182
1183////////////////////////////////////////////////////////////////////////////////
1184/// Search WS handler with given name
1185///
1186/// Handler must be registered with RegisterWS() method
1187
1188std::shared_ptr<THttpWSHandler> THttpServer::FindWS(const char *name)
1189{
1190 std::lock_guard<std::mutex> grd(fWSMutex);
1191 for (auto &ws : fWSHandlers) {
1192 if (strcmp(name, ws->GetName()) == 0)
1193 return ws;
1194 }
1195
1196 return nullptr;
1197}
1198
1199////////////////////////////////////////////////////////////////////////////////
1200/// Execute WS related operation
1201
1202Bool_t THttpServer::ExecuteWS(std::shared_ptr<THttpCallArg> &arg, Bool_t external_thrd, Bool_t wait_process)
1203{
1204 if (fTerminated) {
1205 arg->Set404();
1206 return kFALSE;
1207 }
1208
1209 auto wsptr = FindWS(arg->GetPathName());
1210
1211 auto handler = wsptr.get();
1212
1213 if (!handler && !external_thrd)
1214 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1215
1216 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1217
1218 if (fTimer && fTimer->IsSlow())
1219 fTimer->SetSlow(kFALSE);
1220
1221 std::unique_lock<std::mutex> lk(fMutex);
1222 fArgs.push(arg);
1223 // and now wait until request is processed
1224 if (wait_process)
1225 arg->fCond.wait(lk);
1226
1227 return kTRUE;
1228 }
1229
1230 if (!handler)
1231 return kFALSE;
1232
1233 Bool_t process = kFALSE;
1234
1235 if (arg->fFileName == "root.websocket") {
1236 // handling of web socket
1237 process = handler->HandleWS(arg);
1238 } else if (arg->fFileName == "root.longpoll") {
1239 // ROOT emulation of websocket with polling requests
1240 if (arg->fQuery.BeginsWith("raw_connect") || arg->fQuery.BeginsWith("txt_connect")) {
1241 // try to emulate websocket connect
1242 // if accepted, reply with connection id, which must be used in the following communications
1243 arg->SetMethod("WS_CONNECT");
1244
1245 bool israw = arg->fQuery.BeginsWith("raw_connect");
1246
1247 // automatically assign engine to arg
1248 arg->CreateWSEngine<THttpLongPollEngine>(israw);
1249
1250 if (handler->HandleWS(arg)) {
1251 arg->SetMethod("WS_READY");
1252
1253 if (handler->HandleWS(arg))
1254 arg->SetTextContent(std::string(israw ? "txt:" : "") + std::to_string(arg->GetWSId()));
1255 } else {
1256 arg->TakeWSEngine(); // delete handle
1257 }
1258
1259 process = arg->IsText();
1260 } else {
1261 TUrl url;
1262 url.SetOptions(arg->fQuery);
1263 url.ParseOptions();
1264 const char *connid = url.GetValueFromOptions("connection");
1265 if (connid)
1266 arg->SetWSId(std::stoul(connid));
1267 if (url.HasOption("close")) {
1268 arg->SetMethod("WS_CLOSE");
1269 arg->SetTextContent("OK");
1270 } else {
1271 arg->SetMethod("WS_DATA");
1272 }
1273
1274 process = handler->HandleWS(arg);
1275 }
1276 }
1277
1278 if (!process)
1279 arg->Set404();
1280
1281 return process;
1282}
1283
1284////////////////////////////////////////////////////////////////////////////////
1285/// Restrict access to specified object
1286///
1287/// See TRootSniffer::Restrict() for more details
1288
1289void THttpServer::Restrict(const char *path, const char *options)
1290{
1291 fSniffer->Restrict(path, options);
1292}
1293
1294////////////////////////////////////////////////////////////////////////////////
1295/// Register command which can be executed from web interface
1296///
1297/// As method one typically specifies string, which is executed with
1298/// gROOT->ProcessLine() method. For instance:
1299///
1300/// serv->RegisterCommand("Invoke","InvokeFunction()");
1301///
1302/// Or one could specify any method of the object which is already registered
1303/// to the server. For instance:
1304///
1305/// serv->Register("/", hpx);
1306/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1307///
1308/// Here symbols '/->' separates item name from method to be executed
1309///
1310/// One could specify additional arguments in the command with
1311/// syntax like %arg1%, %arg2% and so on. For example:
1312///
1313/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1314/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1315///
1316/// Such parameter(s) will be requested when command clicked in the browser.
1317///
1318/// Once command is registered, one could specify icon which will appear in the browser:
1319///
1320/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1321///
1322/// One also can set extra property '_fastcmd', that command appear as
1323/// tool button on the top of the browser tree:
1324///
1325/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1326///
1327/// Or it is equivalent to specifying extra argument when register command:
1328///
1329/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1330
1331Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1332{
1333 return fSniffer->RegisterCommand(cmdname, method, icon);
1334}
1335
1336////////////////////////////////////////////////////////////////////////////////
1337/// Hides folder or element from web gui
1338
1339Bool_t THttpServer::Hide(const char *foldername, Bool_t hide)
1340{
1341 return SetItemField(foldername, "_hidden", hide ? "true" : (const char *)0);
1342}
1343
1344////////////////////////////////////////////////////////////////////////////////
1345/// Set name of icon, used in browser together with the item
1346///
1347/// One could use images from $ROOTSYS directory like:
1348/// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
1349
1350Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
1351{
1352 return SetItemField(fullname, "_icon", iconname);
1353}
1354
1355////////////////////////////////////////////////////////////////////////////////
1356/// Create item in sniffer
1357
1358Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
1359{
1360 return fSniffer->CreateItem(fullname, title);
1361}
1362
1363////////////////////////////////////////////////////////////////////////////////
1364/// Set item field in sniffer
1365
1366Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
1367{
1368 return fSniffer->SetItemField(fullname, name, value);
1369}
1370
1371////////////////////////////////////////////////////////////////////////////////
1372/// Get item field from sniffer
1373
1374const char *THttpServer::GetItemField(const char *fullname, const char *name)
1375{
1376 return fSniffer->GetItemField(fullname, name);
1377}
1378
1379////////////////////////////////////////////////////////////////////////////////
1380/// Returns MIME type base on file extension
1381
1382const char *THttpServer::GetMimeType(const char *path)
1383{
1384 static const struct {
1385 const char *extension;
1386 int ext_len;
1387 const char *mime_type;
1388 } builtin_mime_types[] = {{".xml", 4, "text/xml"},
1389 {".json", 5, "application/json"},
1390 {".bin", 4, "application/x-binary"},
1391 {".gif", 4, "image/gif"},
1392 {".jpg", 4, "image/jpeg"},
1393 {".png", 4, "image/png"},
1394 {".html", 5, "text/html"},
1395 {".htm", 4, "text/html"},
1396 {".shtm", 5, "text/html"},
1397 {".shtml", 6, "text/html"},
1398 {".css", 4, "text/css"},
1399 {".js", 3, "application/x-javascript"},
1400 {".mjs", 4, "text/javascript"},
1401 {".ico", 4, "image/x-icon"},
1402 {".jpeg", 5, "image/jpeg"},
1403 {".svg", 4, "image/svg+xml"},
1404 {".txt", 4, "text/plain"},
1405 {".torrent", 8, "application/x-bittorrent"},
1406 {".wav", 4, "audio/x-wav"},
1407 {".mp3", 4, "audio/x-mp3"},
1408 {".mid", 4, "audio/mid"},
1409 {".m3u", 4, "audio/x-mpegurl"},
1410 {".ogg", 4, "application/ogg"},
1411 {".ram", 4, "audio/x-pn-realaudio"},
1412 {".xslt", 5, "application/xml"},
1413 {".xsl", 4, "application/xml"},
1414 {".ra", 3, "audio/x-pn-realaudio"},
1415 {".doc", 4, "application/msword"},
1416 {".exe", 4, "application/octet-stream"},
1417 {".zip", 4, "application/x-zip-compressed"},
1418 {".xls", 4, "application/excel"},
1419 {".tgz", 4, "application/x-tar-gz"},
1420 {".tar", 4, "application/x-tar"},
1421 {".gz", 3, "application/x-gunzip"},
1422 {".arj", 4, "application/x-arj-compressed"},
1423 {".rar", 4, "application/x-arj-compressed"},
1424 {".rtf", 4, "application/rtf"},
1425 {".pdf", 4, "application/pdf"},
1426 {".swf", 4, "application/x-shockwave-flash"},
1427 {".mpg", 4, "video/mpeg"},
1428 {".webm", 5, "video/webm"},
1429 {".mpeg", 5, "video/mpeg"},
1430 {".mov", 4, "video/quicktime"},
1431 {".mp4", 4, "video/mp4"},
1432 {".m4v", 4, "video/x-m4v"},
1433 {".asf", 4, "video/x-ms-asf"},
1434 {".avi", 4, "video/x-msvideo"},
1435 {".bmp", 4, "image/bmp"},
1436 {".ttf", 4, "application/x-font-ttf"},
1437 {".woff", 5, "font/woff"},
1438 {".woff2", 6, "font/woff2"},
1439 {NULL, 0, NULL}};
1440
1441 int path_len = strlen(path);
1442
1443 for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
1444 if (path_len <= builtin_mime_types[i].ext_len)
1445 continue;
1446 const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
1447 if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
1448 return builtin_mime_types[i].mime_type;
1449 }
1450 }
1451
1452 return "text/plain";
1453}
1454
1455////////////////////////////////////////////////////////////////////////////////
1456/// Reads file content
1457///
1458/// @deprecated
1459
1461{
1462 len = 0;
1463
1464 std::ifstream is(filename, std::ios::in | std::ios::binary);
1465 if (!is)
1466 return nullptr;
1467
1468 is.seekg(0, is.end);
1469 len = is.tellg();
1470 is.seekg(0, is.beg);
1471
1472 char *buf = (char *)malloc(len);
1473 is.read(buf, len);
1474 if (!is) {
1475 free(buf);
1476 len = 0;
1477 return nullptr;
1478 }
1479
1480 return buf;
1481}
1482
1483////////////////////////////////////////////////////////////////////////////////
1484/// Reads file content, using std::string as container
1485
1486std::string THttpServer::ReadFileContent(const std::string &filename)
1487{
1488 std::ifstream is(filename, std::ios::in | std::ios::binary);
1489 std::string res;
1490 if (is) {
1491 is.seekg(0, std::ios::end);
1492 res.resize(is.tellg());
1493 is.seekg(0, std::ios::beg);
1494 is.read((char *)res.data(), res.length());
1495 if (!is)
1496 res.clear();
1497 }
1498 return res;
1499}
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:597
#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:83
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:2979
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3062
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