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