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 bool first = true;
935 TString importmap = "<script type=\"importmap\">\n{\n \"imports\": ";
936 for (auto &entry : modules) {
937 importmap.Append(TString::Format("%s\n \"%s\": \"%smodules/%s\"", first ? "{" : ",", entry.first.c_str(), jsroot_prefix.c_str(), entry.second.c_str()));
938 first = false;
939 }
940 importmap.Append(TString::Format(",\n \"jsrootsys/\": \"%s\"", jsroot_prefix.c_str()));
941
942 for (auto &entry : fLocations)
943 if (entry.first != "jsrootsys/")
944 importmap.Append(TString::Format(",\n \"%s\": \"%s%s\"", entry.first.c_str(), path_prefix.c_str(), entry.first.c_str()));
945 importmap.Append("\n }\n}\n</script>\n");
946
947 arg->fContent.erase(p, place_holder.length());
948
949 arg->fContent.insert(p, importmap.Data());
950 }
951}
952
953////////////////////////////////////////////////////////////////////////////////
954/// Process single http request
955///
956/// Depending from requested path and filename different actions will be performed.
957/// In most cases information is provided by TRootSniffer class
958
959void THttpServer::ProcessRequest(std::shared_ptr<THttpCallArg> arg)
960{
961 if (fTerminated) {
962 arg->Set404();
963 return;
964 }
965
966 if ((arg->fFileName == "root.websocket") || (arg->fFileName == "root.longpoll")) {
967 ExecuteWS(arg);
968 return;
969 }
970
971 if (arg->fFileName.IsNull() || (arg->fFileName == "index.htm") || (arg->fFileName == "default.htm")) {
972
973 std::string version;
974
975 if (arg->fFileName == "default.htm") {
976
977 if (!IsWSOnly())
978 arg->fContent = ReadFileContent((fJSROOTSYS + "/files/online.htm").Data());
979
980 } else {
981 auto wsptr = FindWS(arg->GetPathName());
982
983 auto handler = wsptr.get();
984
985 if (!handler)
986 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
987
988 if (handler) {
989
990 arg->fContent = handler->GetDefaultPageContent().Data();
991
992 if (arg->fContent.find("file:") == 0) {
993 const char *fname = arg->fContent.c_str() + 5;
994 TString resolve;
995 if (!IsFileRequested(fname, resolve)) resolve = fname;
996 arg->fContent = ReadFileContent(resolve.Data());
997 }
998
999 version = handler->GetCodeVersion();
1000
1001 handler->VerifyDefaultPageContent(arg);
1002 }
1003 }
1004
1005 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() && IsWSOnly()) {
1006 // Creating page with list of available widgets is disabled now for security reasons
1007 // Later one can provide functionality back only if explicitly desired by the user
1008 // BuildWSEntryPage();
1009
1010 arg->SetContent("refused");
1011 arg->Set404();
1012 }
1013
1014 if (arg->fContent.empty() && !IsWSOnly()) {
1015
1016 if (fDefaultPageCont.empty())
1018
1019 arg->fContent = fDefaultPageCont;
1020 }
1021
1022 if (arg->fContent.empty()) {
1023
1024 arg->Set404();
1025 } else if (!arg->Is404()) {
1026
1027 ReplaceJSROOTLinks(arg, version);
1028
1029 const char *hjsontag = "\"$$$h.json$$$\"";
1030
1031 // add h.json caching
1032 if (arg->fContent.find(hjsontag) != std::string::npos) {
1033 TString h_json;
1034 TRootSnifferStoreJson store(h_json, kTRUE);
1035 const char *topname = fTopName.Data();
1036 if (arg->fTopName.Length() > 0)
1037 topname = arg->fTopName.Data();
1038 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1039
1040 arg->ReplaceAllinContent(hjsontag, h_json.Data());
1041
1042 arg->AddNoCacheHeader();
1043
1044 if (arg->fQuery.Index("nozip") == kNPOS)
1045 arg->SetZipping();
1046 }
1047 arg->SetContentType("text/html");
1048 }
1049 return;
1050 }
1051
1052 if ((arg->fFileName == "draw.htm") && !IsWSOnly()) {
1053 if (fDrawPageCont.empty())
1055
1056 if (fDrawPageCont.empty()) {
1057 arg->Set404();
1058 } else {
1059 const char *rootjsontag = "\"$$$root.json$$$\"";
1060 const char *hjsontag = "\"$$$h.json$$$\"";
1061
1062 arg->fContent = fDrawPageCont;
1063
1064 ReplaceJSROOTLinks(arg);
1065
1066 if ((arg->fQuery.Index("no_h_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
1067 (arg->fContent.find(hjsontag) != std::string::npos)) {
1068 TString h_json;
1069 TRootSnifferStoreJson store(h_json, kTRUE);
1070 const char *topname = fTopName.Data();
1071 if (arg->fTopName.Length() > 0)
1072 topname = arg->fTopName.Data();
1073 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
1074
1075 arg->ReplaceAllinContent(hjsontag, h_json.Data());
1076 }
1077
1078 if ((arg->fQuery.Index("no_root_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
1079 (arg->fContent.find(rootjsontag) != std::string::npos)) {
1080 std::string str;
1081 if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=23", str))
1082 arg->ReplaceAllinContent(rootjsontag, str);
1083 }
1084 arg->AddNoCacheHeader();
1085 if (arg->fQuery.Index("nozip") == kNPOS)
1086 arg->SetZipping();
1087 arg->SetContentType("text/html");
1088 }
1089 return;
1090 }
1091
1092 if ((arg->fFileName == "favicon.ico") && arg->fPathName.IsNull()) {
1093 arg->SetFile(fJSROOTSYS + "/img/RootIcon.ico");
1094 return;
1095 }
1096
1098 if (IsFileRequested(arg->fFileName.Data(), filename)) {
1099 arg->SetFile(filename);
1100 return;
1101 }
1102
1103 // check if websocket handler may serve file request
1104 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
1105 TString wsname = arg->fPathName, fname;
1106 auto pos = wsname.First('/');
1107 if (pos == kNPOS) {
1108 wsname = arg->fPathName;
1109 } else {
1110 wsname = arg->fPathName(0, pos);
1111 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
1112 fname.Append("/");
1113 }
1114
1115 fname.Append(arg->fFileName);
1116
1117 if (VerifyFilePath(fname.Data())) {
1118
1119 auto ws = FindWS(wsname.Data());
1120
1121 if (ws && ws->CanServeFiles()) {
1122 TString fdir = ws->GetDefaultPageContent();
1123 // only when file is specified, can take directory, append prefix and file name
1124 if (fdir.Index("file:") == 0) {
1125 fdir.Remove(0, 5);
1126 auto separ = fdir.Last('/');
1127 if (separ != kNPOS)
1128 fdir.Resize(separ + 1);
1129 else
1130 fdir = "./";
1131
1132 fdir.Append(fname);
1133 arg->SetFile(fdir);
1134 return;
1135 }
1136 }
1137 }
1138 }
1139
1140 filename = arg->fFileName;
1141
1142 Bool_t iszip = kFALSE;
1143 if (filename.EndsWith(".gz")) {
1144 filename.Resize(filename.Length() - 3);
1145 iszip = kTRUE;
1146 }
1147
1148 if (IsWSOnly()) {
1149 if (arg->fContent.empty())
1150 arg->Set404();
1151 } else if ((filename == "h.xml") || (filename == "get.xml")) {
1152
1153 Bool_t compact = arg->fQuery.Index("compact") != kNPOS;
1154
1155 TString res;
1156
1157 res.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1158 if (!compact)
1159 res.Append("\n");
1160 res.Append("<root>");
1161 if (!compact)
1162 res.Append("\n");
1163 {
1164 TRootSnifferStoreXml store(res, compact);
1165
1166 const char *topname = fTopName.Data();
1167 if (arg->fTopName.Length() > 0)
1168 topname = arg->fTopName.Data();
1169 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
1170 }
1171
1172 res.Append("</root>");
1173 if (!compact)
1174 res.Append("\n");
1175
1176 arg->SetContent(std::string(res.Data()));
1177
1178 arg->SetXml();
1179 } else if (filename == "h.json") {
1180 TString res;
1181 TRootSnifferStoreJson store(res, arg->fQuery.Index("compact") != kNPOS);
1182 const char *topname = fTopName.Data();
1183 if (arg->fTopName.Length() > 0)
1184 topname = arg->fTopName.Data();
1185 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1186 arg->SetContent(std::string(res.Data()));
1187 arg->SetJson();
1188 } else if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), arg->fContent)) {
1189 // define content type base on extension
1190 arg->SetContentType(GetMimeType(filename.Data()));
1191 } else {
1192 // miss request, user may process
1193 MissedRequest(arg.get());
1194 }
1195
1196 if (arg->Is404())
1197 return;
1198
1199 if (iszip)
1200 arg->SetZipping(THttpCallArg::kZipAlways);
1201
1202 if (filename == "root.bin") {
1203 // only for binary data master version is important
1204 // it allows to detect if streamer info was modified
1205 const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
1206 arg->AddHeader(parname, TString::Format("%u", (unsigned)fSniffer->GetStreamerInfoHash()).Data());
1207 }
1208
1209 // try to avoid caching on the browser
1210 arg->AddNoCacheHeader();
1211
1212 // potentially add cors headers
1213 if (IsCors())
1214 arg->AddHeader("Access-Control-Allow-Origin", GetCors());
1215 if (IsCorsCredentials())
1216 arg->AddHeader("Access-Control-Allow-Credentials", GetCorsCredentials());
1217}
1218
1219////////////////////////////////////////////////////////////////////////////////
1220/// Register object in folders hierarchy
1221///
1222/// See TRootSniffer::RegisterObject() for more details
1223
1224Bool_t THttpServer::Register(const char *subfolder, TObject *obj)
1225{
1226 return fSniffer->RegisterObject(subfolder, obj);
1227}
1228
1229////////////////////////////////////////////////////////////////////////////////
1230/// Unregister object in folders hierarchy
1231///
1232/// See TRootSniffer::UnregisterObject() for more details
1233
1235{
1236 return fSniffer->UnregisterObject(obj);
1237}
1238
1239////////////////////////////////////////////////////////////////////////////////
1240/// Register WS handler to the THttpServer
1241///
1242/// Only such handler can be used in multi-threaded processing of websockets
1243
1244void THttpServer::RegisterWS(std::shared_ptr<THttpWSHandler> ws)
1245{
1246 std::lock_guard<std::mutex> grd(fWSMutex);
1247 fWSHandlers.emplace_back(ws);
1248}
1249
1250////////////////////////////////////////////////////////////////////////////////
1251/// Unregister WS handler to the THttpServer
1252
1253void THttpServer::UnregisterWS(std::shared_ptr<THttpWSHandler> ws)
1254{
1255 std::lock_guard<std::mutex> grd(fWSMutex);
1256 for (int n = (int)fWSHandlers.size(); n > 0; --n)
1257 if ((fWSHandlers[n - 1] == ws) || fWSHandlers[n - 1]->IsDisabled())
1258 fWSHandlers.erase(fWSHandlers.begin() + n - 1);
1259}
1260
1261////////////////////////////////////////////////////////////////////////////////
1262/// Search WS handler with given name
1263///
1264/// Handler must be registered with RegisterWS() method
1265
1266std::shared_ptr<THttpWSHandler> THttpServer::FindWS(const char *name)
1267{
1268 std::lock_guard<std::mutex> grd(fWSMutex);
1269 for (auto &ws : fWSHandlers) {
1270 if (strcmp(name, ws->GetName()) == 0)
1271 return ws;
1272 }
1273
1274 return nullptr;
1275}
1276
1277////////////////////////////////////////////////////////////////////////////////
1278/// Execute WS related operation
1279
1280Bool_t THttpServer::ExecuteWS(std::shared_ptr<THttpCallArg> &arg, Bool_t external_thrd, Bool_t wait_process)
1281{
1282 if (fTerminated) {
1283 arg->Set404();
1284 return kFALSE;
1285 }
1286
1287 auto wsptr = FindWS(arg->GetPathName());
1288
1289 auto handler = wsptr.get();
1290
1291 if (!handler && !external_thrd)
1292 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1293
1294 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1295
1296 if (fTimer && fTimer->IsSlow())
1297 fTimer->SetSlow(kFALSE);
1298
1299 std::unique_lock<std::mutex> lk(fMutex);
1300 fArgs.push(arg);
1301 // and now wait until request is processed
1302 if (wait_process)
1303 arg->fCond.wait(lk);
1304
1305 return kTRUE;
1306 }
1307
1308 if (!handler)
1309 return kFALSE;
1310
1311 Bool_t process = kFALSE;
1312
1313 if (arg->fFileName == "root.websocket") {
1314 // handling of web socket
1315 process = handler->HandleWS(arg);
1316 } else if (arg->fFileName == "root.longpoll") {
1317 // ROOT emulation of websocket with polling requests
1318 if (arg->fQuery.BeginsWith("raw_connect") || arg->fQuery.BeginsWith("txt_connect")) {
1319 // try to emulate websocket connect
1320 // if accepted, reply with connection id, which must be used in the following communications
1321 arg->SetMethod("WS_CONNECT");
1322
1323 bool israw = arg->fQuery.BeginsWith("raw_connect");
1324
1325 // automatically assign engine to arg
1326 arg->CreateWSEngine<THttpLongPollEngine>(israw);
1327
1328 if (handler->HandleWS(arg)) {
1329 arg->SetMethod("WS_READY");
1330
1331 if (handler->HandleWS(arg))
1332 arg->SetTextContent(std::string(israw ? "txt:" : "") + std::to_string(arg->GetWSId()));
1333 } else {
1334 arg->TakeWSEngine(); // delete handle
1335 }
1336
1337 process = arg->IsText();
1338 } else {
1339 TUrl url;
1340 url.SetOptions(arg->fQuery);
1341 url.ParseOptions();
1342 const char *connid = url.GetValueFromOptions("connection");
1343 if (connid)
1344 arg->SetWSId(std::stoul(connid));
1345 if (url.HasOption("close")) {
1346 arg->SetMethod("WS_CLOSE");
1347 arg->SetTextContent("OK");
1348 } else {
1349 arg->SetMethod("WS_DATA");
1350 }
1351
1352 process = handler->HandleWS(arg);
1353 }
1354 }
1355
1356 if (!process)
1357 arg->Set404();
1358
1359 return process;
1360}
1361
1362////////////////////////////////////////////////////////////////////////////////
1363/// Restrict access to specified object
1364///
1365/// See TRootSniffer::Restrict() for more details
1366
1367void THttpServer::Restrict(const char *path, const char *options)
1368{
1369 fSniffer->Restrict(path, options);
1370}
1371
1372////////////////////////////////////////////////////////////////////////////////
1373/// Register command which can be executed from web interface
1374///
1375/// As method one typically specifies string, which is executed with
1376/// gROOT->ProcessLine() method. For instance:
1377///
1378/// serv->RegisterCommand("Invoke","InvokeFunction()");
1379///
1380/// Or one could specify any method of the object which is already registered
1381/// to the server. For instance:
1382///
1383/// serv->Register("/", hpx);
1384/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1385///
1386/// Here symbols '/->' separates item name from method to be executed
1387///
1388/// One could specify additional arguments in the command with
1389/// syntax like %arg1%, %arg2% and so on. For example:
1390///
1391/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1392/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1393///
1394/// Such parameter(s) will be requested when command clicked in the browser.
1395///
1396/// Once command is registered, one could specify icon which will appear in the browser:
1397///
1398/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1399///
1400/// One also can set extra property '_fastcmd', that command appear as
1401/// tool button on the top of the browser tree:
1402///
1403/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1404///
1405/// Or it is equivalent to specifying extra argument when register command:
1406///
1407/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1408
1409Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1410{
1411 return fSniffer->RegisterCommand(cmdname, method, icon);
1412}
1413
1414////////////////////////////////////////////////////////////////////////////////
1415/// Hides folder or element from web gui
1416
1417Bool_t THttpServer::Hide(const char *foldername, Bool_t hide)
1418{
1419 return SetItemField(foldername, "_hidden", hide ? "true" : (const char *)0);
1420}
1421
1422////////////////////////////////////////////////////////////////////////////////
1423/// Set name of icon, used in browser together with the item
1424///
1425/// One could use images from $ROOTSYS directory like:
1426/// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
1427
1428Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
1429{
1430 return SetItemField(fullname, "_icon", iconname);
1431}
1432
1433////////////////////////////////////////////////////////////////////////////////
1434/// Create item in sniffer
1435
1436Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
1437{
1438 return fSniffer->CreateItem(fullname, title);
1439}
1440
1441////////////////////////////////////////////////////////////////////////////////
1442/// Set item field in sniffer
1443
1444Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
1445{
1446 return fSniffer->SetItemField(fullname, name, value);
1447}
1448
1449////////////////////////////////////////////////////////////////////////////////
1450/// Get item field from sniffer
1451
1452const char *THttpServer::GetItemField(const char *fullname, const char *name)
1453{
1454 return fSniffer->GetItemField(fullname, name);
1455}
1456
1457////////////////////////////////////////////////////////////////////////////////
1458/// Returns MIME type base on file extension
1459
1460const char *THttpServer::GetMimeType(const char *path)
1461{
1462 static const struct {
1463 const char *extension;
1464 int ext_len;
1465 const char *mime_type;
1466 } builtin_mime_types[] = {{".xml", 4, "text/xml"},
1467 {".json", 5, "application/json"},
1468 {".bin", 4, "application/x-binary"},
1469 {".gif", 4, "image/gif"},
1470 {".jpg", 4, "image/jpeg"},
1471 {".png", 4, "image/png"},
1472 {".html", 5, "text/html"},
1473 {".htm", 4, "text/html"},
1474 {".shtm", 5, "text/html"},
1475 {".shtml", 6, "text/html"},
1476 {".css", 4, "text/css"},
1477 {".js", 3, "application/x-javascript"},
1478 {".mjs", 4, "text/javascript"},
1479 {".ico", 4, "image/x-icon"},
1480 {".jpeg", 5, "image/jpeg"},
1481 {".svg", 4, "image/svg+xml"},
1482 {".txt", 4, "text/plain"},
1483 {".torrent", 8, "application/x-bittorrent"},
1484 {".wav", 4, "audio/x-wav"},
1485 {".mp3", 4, "audio/x-mp3"},
1486 {".mid", 4, "audio/mid"},
1487 {".m3u", 4, "audio/x-mpegurl"},
1488 {".ogg", 4, "application/ogg"},
1489 {".ram", 4, "audio/x-pn-realaudio"},
1490 {".xslt", 5, "application/xml"},
1491 {".xsl", 4, "application/xml"},
1492 {".ra", 3, "audio/x-pn-realaudio"},
1493 {".doc", 4, "application/msword"},
1494 {".exe", 4, "application/octet-stream"},
1495 {".zip", 4, "application/x-zip-compressed"},
1496 {".xls", 4, "application/excel"},
1497 {".tgz", 4, "application/x-tar-gz"},
1498 {".tar", 4, "application/x-tar"},
1499 {".gz", 3, "application/x-gunzip"},
1500 {".arj", 4, "application/x-arj-compressed"},
1501 {".rar", 4, "application/x-arj-compressed"},
1502 {".rtf", 4, "application/rtf"},
1503 {".pdf", 4, "application/pdf"},
1504 {".swf", 4, "application/x-shockwave-flash"},
1505 {".mpg", 4, "video/mpeg"},
1506 {".webm", 5, "video/webm"},
1507 {".mpeg", 5, "video/mpeg"},
1508 {".mov", 4, "video/quicktime"},
1509 {".mp4", 4, "video/mp4"},
1510 {".m4v", 4, "video/x-m4v"},
1511 {".asf", 4, "video/x-ms-asf"},
1512 {".avi", 4, "video/x-msvideo"},
1513 {".bmp", 4, "image/bmp"},
1514 {".ttf", 4, "application/x-font-ttf"},
1515 {".woff", 5, "font/woff"},
1516 {".woff2", 6, "font/woff2"},
1517 {NULL, 0, NULL}};
1518
1519 int path_len = strlen(path);
1520
1521 for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
1522 if (path_len <= builtin_mime_types[i].ext_len)
1523 continue;
1524 const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
1525 if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
1526 return builtin_mime_types[i].mime_type;
1527 }
1528 }
1529
1530 return "text/plain";
1531}
1532
1533////////////////////////////////////////////////////////////////////////////////
1534/// Reads file content
1535///
1536/// @deprecated
1537
1539{
1540 len = 0;
1541
1542 std::ifstream is(filename, std::ios::in | std::ios::binary);
1543 if (!is)
1544 return nullptr;
1545
1546 is.seekg(0, is.end);
1547 len = is.tellg();
1548 is.seekg(0, is.beg);
1549
1550 char *buf = (char *)malloc(len);
1551 is.read(buf, len);
1552 if (!is) {
1553 free(buf);
1554 len = 0;
1555 return nullptr;
1556 }
1557
1558 return buf;
1559}
1560
1561////////////////////////////////////////////////////////////////////////////////
1562/// Reads file content, using std::string as container
1563
1564std::string THttpServer::ReadFileContent(const std::string &filename)
1565{
1566 std::ifstream is(filename, std::ios::in | std::ios::binary);
1567 std::string res;
1568 if (is) {
1569 is.seekg(0, std::ios::end);
1570 res.resize(is.tellg());
1571 is.seekg(0, std::ios::beg);
1572 is.read((char *)res.data(), res.length());
1573 if (!is)
1574 res.clear();
1575 }
1576 return res;
1577}
long Long_t
Definition RtypesCore.h:54
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
constexpr Ssiz_t kNPOS
Definition RtypesCore.h:124
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
#define ClassImp(name)
Definition Rtypes.h:377
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
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:439
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:973
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
static const TString & GetRootSys()
Get the rootsys directory in the installation. Static utility function.
Definition TROOT.cxx:2979
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3062
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