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