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