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