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