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