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