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\brief Online http server for arbitrary ROOT application
62\note This class provides HTTP access to ROOT objects. The user is entirely responsible for the security of the server.
63It is strongly recommended to use the server only within an isolated network
64or to enable proper authentication to prevent unauthorized remote access.
65
66Idea of THttpServer - provide remote http access to running
67ROOT application and enable HTML/JavaScript user interface.
68Any registered object can be requested and displayed in the browser.
69There are many benefits of such approach:
70
711. standard http interface to ROOT application
722. no any temporary ROOT files when access data
733. user interface running in all browsers
74
75To start http server simply create instance
76of the THttpServer class like:
77
78 serv = new THttpServer("http:8080");
79
80This will starts civetweb-based http server with http port 8080.
81Than one should be able to open address "http://localhost:8080"
82in any modern web browser (Firefox, Chrome, Opera, ...) and browse objects,
83created in ROOT application. By default, server can access files,
84canvases and histograms via `gROOT` pointer. All such objects
85can be displayed with JSROOT graphics.
86
87At any time one could register other objects with the command:
88
89 TGraph* gr = new TGraph(10);
90 gr->SetName("gr1");
91 serv->Register("graphs/subfolder", gr);
92
93If objects content is changing in the application, one could
94enable monitoring flag in the browser - than objects view
95will be regularly updated.
96
97More information: https://root.cern/root/htmldoc/guides/HttpServer/HttpServer.html
98*/
99
100
101////////////////////////////////////////////////////////////////////////////////
102/// constructor
103///
104/// As argument, one specifies engine kind which should be
105/// created like "http:8080". One could specify several engines
106/// at once, separating them with semicolon (";"). Following engines are supported:
107///
108/// http - TCivetweb, civetweb-based implementation of http protocol
109/// socket - TCivetweb bound to unix socket
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 civetweb web server bound with unix socket
416/// serv->CreateEngine("socket:/home/user/server.socket?socket_mode=0700");
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 // add call arg to the list
661 std::unique_lock<std::mutex> lk(fMutex);
662 arg->fNotifyFlag = kFALSE;
663 fArgs.push(arg);
664 // and now wait until request is processed
665 arg->fCond.wait(lk);
666
667 return kTRUE;
668}
669
670////////////////////////////////////////////////////////////////////////////////
671/// Submit http request, specified in THttpCallArg structure
672///
673/// Contrary to ExecuteHttp, it will not block calling thread.
674/// User should implement THttpCallArg::HttpReplied() method
675/// to react when HTTP request is executed.
676
677/// Method can be called from any thread
678/// Actual execution will be done in main ROOT thread, where analysis code is running.
679/// When called from main thread and can_run_immediately==kTRUE, will be
680/// executed immediately.
681///
682/// Returns kTRUE when was executed.
683
684Bool_t THttpServer::SubmitHttp(std::shared_ptr<THttpCallArg> arg, Bool_t can_run_immediately)
685{
686 if (fTerminated)
687 return kFALSE;
688
690 ProcessRequest(arg);
691 arg->NotifyCondition();
692 return kTRUE;
693 }
694
695 // add call arg to the list
696 std::unique_lock<std::mutex> lk(fMutex);
697 fArgs.push(arg);
698 return kFALSE;
699}
700
701////////////////////////////////////////////////////////////////////////////////
702/// Process requests, submitted for execution
703///
704/// Returns number of processed requests
705///
706/// Normally invoked by THttpTimer, when somewhere in the code
707/// gSystem->ProcessEvents() is called.
708/// User can call serv->ProcessRequests() directly, but only from main thread.
709/// If special server thread is created, called from that thread
710
712{
713 auto id = TThread::SelfId();
714
715 if (fMainThrdId != id) {
716 if (gDebug > 0 && fMainThrdId)
717 Warning("ProcessRequests", "Changing main thread to %ld", (long)id);
718 fMainThrdId = id;
719 }
720
722
723 if (fProcessingThrdId) {
724 if (fProcessingThrdId == id) {
726 } else {
727 Error("ProcessRequests", "Processing already running from %ld thread", (long) fProcessingThrdId);
728 return 0;
729 }
730 }
731
732 if (!recursion)
734
735 Int_t cnt = 0;
736
737 std::unique_lock<std::mutex> lk(fMutex, std::defer_lock);
738
739 // first process requests in the queue
740 while (true) {
741 std::shared_ptr<THttpCallArg> arg;
742
743 lk.lock();
744 if (!fArgs.empty()) {
745 arg = fArgs.front();
746 fArgs.pop();
747 }
748 lk.unlock();
749
750 if (!arg)
751 break;
752
753 if (arg->fFileName == "root_batch_holder.js") {
755 continue;
756 }
757
758 auto prev = fSniffer->SetCurrentCallArg(arg.get());
759
760 try {
761 cnt++;
762 ProcessRequest(arg);
763 fSniffer->SetCurrentCallArg(prev);
764 } catch (...) {
765 fSniffer->SetCurrentCallArg(prev);
766 }
767
768 arg->NotifyCondition();
769 }
770
771 // regularly call Process() method of engine to let perform actions in ROOT context
772 TIter iter(&fEngines);
773 while (auto engine = static_cast<THttpEngine *>(iter())) {
774 if (fTerminated)
775 engine->Terminate();
776 engine->Process();
777 }
778
779 if (!recursion)
781
782 return cnt;
783}
784
785////////////////////////////////////////////////////////////////////////////////
786/// Method called when THttpServer cannot process request
787///
788/// By default such requests replied with 404 code
789/// One could overwrite with method in derived class to process all kinds of such non-standard requests
790
792{
793 arg->Set404();
794}
795
796////////////////////////////////////////////////////////////////////////////////
797/// Process special http request for root_batch_holder.js script
798///
799/// This kind of requests used to hold web browser running in headless mode
800/// Intentionally requests does not replied immediately
801
802void THttpServer::ProcessBatchHolder(std::shared_ptr<THttpCallArg> &arg)
803{
804 auto wsptr = FindWS(arg->GetPathName());
805
806 if (!wsptr || !wsptr->ProcessBatchHolder(arg)) {
807 arg->Set404();
808 arg->NotifyCondition();
809 }
810}
811
812////////////////////////////////////////////////////////////////////////////////
813/// Create summary page with active WS handlers
814
816{
817
818 std::string arr = "[";
819
820 {
821 std::lock_guard<std::mutex> grd(fWSMutex);
822 for (auto &ws : fWSHandlers) {
823 if (arr.length() > 1)
824 arr.append(", ");
825
826 arr.append(TString::Format("{ name: \"%s\", title: \"%s\" }", ws->GetName(), ws->GetTitle()).Data());
827 }
828 }
829
830 arr.append("]");
831
832 std::string res = ReadFileContent((TROOT::GetDataDir() + "/js/files/wslist.htm").Data());
833
834 std::string arg = "\"$$$wslist$$$\"";
835
836 auto pos = res.find(arg);
837 if (pos != std::string::npos)
838 res.replace(pos, arg.length(), arr);
839
840 return res;
841}
842
843////////////////////////////////////////////////////////////////////////////////
844/// Replaces all references like "jsrootsys/..." or other pre-configured pathes
845///
846/// Either using pre-configured JSROOT installation from web or
847/// redirect to jsrootsys from the main server path to benefit from browser caching
848/// Creates appropriate importmap instead of <!--jsroot_importmap--> placeholder
849
850void THttpServer::ReplaceJSROOTLinks(std::shared_ptr<THttpCallArg> &arg, const std::string &version)
851{
852 const std::string place_holder = "<!--jsroot_importmap-->";
853
854 auto p = arg->fContent.find(place_holder);
855
856 bool old_format = (p == std::string::npos);
857
858 // count slashes to handler relative paths to jsroot
859 Int_t slash_cnt = 0;
860 if (arg->fPathName.Length() > 0)
861 slash_cnt++;
862 for (Int_t n = 1; n < arg->fPathName.Length()-1; ++n)
863 if (arg->fPathName[n] == '/') {
864 if (arg->fPathName[n-1] != '/') {
865 slash_cnt++; // normal slash in the middle, count it
866 } else {
867 slash_cnt = 0; // double slash, do not touch such path
868 break;
869 }
870 }
871
872
873 if (old_format) {
874 // old functionality
875
876 if (!version.empty()) {
877 // replace link to JSROOT modules in import statements emulating new version for browser
878 std::string search = "from './jsrootsys/";
879 std::string replace = "from './" + version + "/jsrootsys/";
880 arg->ReplaceAllinContent(search, replace);
881 // replace link to ROOT ui5 modules in import statements emulating new version for browser
882 search = "from './rootui5sys/";
883 replace = "from './" + version + "/rootui5sys/";
884 arg->ReplaceAllinContent(search, replace);
885 // replace link on old JSRoot.core.js script - if still appears
886 search = "jsrootsys/scripts/JSRoot.core.";
887 replace = version + "/jsrootsys/scripts/JSRoot.core.";
888 arg->ReplaceAllinContent(search, replace, true);
889 arg->AddNoCacheHeader();
890 }
891
892 std::string repl;
893
894 if (fJSROOT.Length() > 0) {
895 repl = "=\"";
896 repl.append(fJSROOT.Data());
897 if (repl.back() != '/')
898 repl.append("/");
899 } else {
900 if (slash_cnt > 0) {
901 repl = "=\"";
902 while (slash_cnt-- > 0) repl.append("../");
903 repl.append("jsrootsys/");
904 }
905 }
906
907 if (!repl.empty()) {
908 arg->ReplaceAllinContent("=\"jsrootsys/", repl);
909 arg->ReplaceAllinContent("from './jsrootsys/", TString::Format("from '%s", repl.substr(2).c_str()).Data());
910 }
911 } else {
912 // new functionality creating importmap
913
914 std::string path_prefix, jsroot_prefix;
915 if (slash_cnt > 0) {
916 path_prefix = "";
917 while (slash_cnt-- > 0)
918 path_prefix.append("../");
919 } else {
920 path_prefix = "./";
921 }
922
923 if (!version.empty())
924 path_prefix.append(version + "/");
925
926 if (fJSROOT.Length() > 0) {
928 if (jsroot_prefix.back() != '/')
929 jsroot_prefix.append("/");
930 } else {
931 jsroot_prefix = path_prefix + "jsrootsys/";
932 }
933
934 static std::map<std::string, std::string> modules = {
935 {"jsroot", "main.mjs"}, {"jsroot/core", "core.mjs"},
936 {"jsroot/io", "io.mjs"}, {"jsroot/tree", "tree.mjs"},
937 {"jsroot/draw", "draw.mjs"}, {"jsroot/gui", "gui.mjs"},
938 {"jsroot/d3", "d3.mjs"}, {"jsroot/three", "three.mjs"}, {"jsroot/three_addons", "three_addons.mjs"},
939 {"jsroot/geom", "geom/TGeoPainter.mjs"}, {"jsroot/hpainter", "gui/HierarchyPainter.mjs"},
940 {"jsroot/webwindow", "webwindow.mjs"}
941 };
942
944 auto pspace = p;
945 while ((--pspace > 0) && (arg->fContent[pspace] == ' ') && (space_prefix.Length() < 20))
946 space_prefix.Append(" ");
947
948 bool first = true;
949 TString importmap = "<script type=\"importmap\">\n";
950 importmap += space_prefix + "{\n";
951 importmap += space_prefix + " \"imports\": ";
952 for (auto &entry : modules) {
953 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()));
954 first = false;
955 }
956 importmap.Append(TString::Format(",\n%s \"jsrootsys/\": \"%s\"", space_prefix.Data(), jsroot_prefix.c_str()));
957
958 for (auto &entry : fLocations)
959 if (entry.first != "jsrootsys/")
960 importmap.Append(TString::Format(",\n%s \"%s\": \"%s%s\"", space_prefix.Data(), entry.first.c_str(), path_prefix.c_str(), entry.first.c_str()));
961 importmap.Append("\n");
962 importmap.Append(space_prefix + " }\n");
963 importmap.Append(space_prefix + "}\n");
964 importmap.Append(space_prefix + "</script>\n");
965
966 arg->fContent.erase(p, place_holder.length());
967
968 arg->fContent.insert(p, importmap.Data());
969 }
970}
971
972////////////////////////////////////////////////////////////////////////////////
973/// Process single http request
974///
975/// Depending from requested path and filename different actions will be performed.
976/// In most cases information is provided by TRootSniffer class
977
978void THttpServer::ProcessRequest(std::shared_ptr<THttpCallArg> arg)
979{
980 if (fTerminated) {
981 arg->Set404();
982 return;
983 }
984
985 if ((arg->fFileName == "root.websocket") || (arg->fFileName == "root.longpoll")) {
986 ExecuteWS(arg);
987 return;
988 }
989
990 if (arg->fFileName.IsNull() || (arg->fFileName == "index.htm") || (arg->fFileName == "default.htm")) {
991
992 std::string version;
993
994 if (arg->fFileName == "default.htm") {
995
996 if (!IsWSOnly())
997 arg->fContent = ReadFileContent((fJSROOTSYS + "/files/online.htm").Data());
998
999 } else {
1000 auto wsptr = FindWS(arg->GetPathName());
1001
1002 auto handler = wsptr.get();
1003
1004 if (!handler)
1005 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1006
1007 if (handler) {
1008
1009 arg->fContent = handler->GetDefaultPageContent().Data();
1010
1011 if (arg->fContent.find("file:") == 0) {
1012 const char *fname = arg->fContent.c_str() + 5;
1015 arg->fContent = ReadFileContent(resolve.Data());
1016 }
1017
1018 version = handler->GetCodeVersion();
1019
1020 handler->VerifyDefaultPageContent(arg);
1021 }
1022 }
1023
1024 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() && IsWSOnly()) {
1025 // Creating page with list of available widgets is disabled now for security reasons
1026 // Later one can provide functionality back only if explicitly desired by the user
1027 // BuildWSEntryPage();
1028
1029 arg->SetContent("refused");
1030 arg->Set404();
1031 }
1032
1033 if (arg->fContent.empty() && !IsWSOnly()) {
1034
1035 if (fDefaultPageCont.empty())
1037
1038 arg->fContent = fDefaultPageCont;
1039 }
1040
1041 if (arg->fContent.empty()) {
1042
1043 arg->Set404();
1044 } else if (!arg->Is404()) {
1045
1047
1048 const char *hjsontag = "\"$$$h.json$$$\"";
1049
1050 // add h.json caching
1051 if (arg->fContent.find(hjsontag) != std::string::npos) {
1054 const char *topname = fTopName.Data();
1055 if (arg->fTopName.Length() > 0)
1056 topname = arg->fTopName.Data();
1057 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1058
1059 arg->ReplaceAllinContent(hjsontag, h_json.Data());
1060
1061 arg->AddNoCacheHeader();
1062
1063 if (arg->fQuery.Index("nozip") == kNPOS)
1064 arg->SetZipping();
1065 }
1066 arg->SetContentType("text/html");
1067 }
1068 return;
1069 }
1070
1071 if ((arg->fFileName == "draw.htm") && !IsWSOnly()) {
1072 if (fDrawPageCont.empty())
1074
1075 if (fDrawPageCont.empty()) {
1076 arg->Set404();
1077 } else {
1078 const char *rootjsontag = "\"$$$root.json$$$\"";
1079 const char *hjsontag = "\"$$$h.json$$$\"";
1080
1081 arg->fContent = fDrawPageCont;
1082
1083 ReplaceJSROOTLinks(arg);
1084
1085 if ((arg->fQuery.Index("no_h_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
1086 (arg->fContent.find(hjsontag) != std::string::npos)) {
1089 const char *topname = fTopName.Data();
1090 if (arg->fTopName.Length() > 0)
1091 topname = arg->fTopName.Data();
1092 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
1093
1094 arg->ReplaceAllinContent(hjsontag, h_json.Data());
1095 }
1096
1097 if ((arg->fQuery.Index("no_root_json") == kNPOS) && (arg->fQuery.Index("webcanvas") == kNPOS) &&
1098 (arg->fContent.find(rootjsontag) != std::string::npos)) {
1099 std::string str;
1100 if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=23", str))
1101 arg->ReplaceAllinContent(rootjsontag, str);
1102 }
1103 arg->AddNoCacheHeader();
1104 if (arg->fQuery.Index("nozip") == kNPOS)
1105 arg->SetZipping();
1106 arg->SetContentType("text/html");
1107 }
1108 return;
1109 }
1110
1111 if ((arg->fFileName == "favicon.ico") && arg->fPathName.IsNull()) {
1112 arg->SetFile(fJSROOTSYS + "/img/RootIcon.ico");
1113 return;
1114 }
1115
1117 if (IsFileRequested(arg->fFileName.Data(), filename)) {
1118 arg->SetFile(filename);
1119 return;
1120 }
1121
1122 // check if websocket handler may serve file request
1123 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
1124 TString wsname = arg->fPathName, fname;
1125 auto pos = wsname.First('/');
1126 if (pos == kNPOS) {
1127 wsname = arg->fPathName;
1128 } else {
1129 wsname = arg->fPathName(0, pos);
1130 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
1131 fname.Append("/");
1132 }
1133
1134 fname.Append(arg->fFileName);
1135
1136 if (VerifyFilePath(fname.Data())) {
1137
1138 auto ws = FindWS(wsname.Data());
1139
1140 if (ws && ws->CanServeFiles()) {
1141 TString fdir = ws->GetDefaultPageContent();
1142 // only when file is specified, can take directory, append prefix and file name
1143 if (fdir.Index("file:") == 0) {
1144 fdir.Remove(0, 5);
1145 auto separ = fdir.Last('/');
1146 if (separ != kNPOS)
1147 fdir.Resize(separ + 1);
1148 else
1149 fdir = "./";
1150
1151 fdir.Append(fname);
1152 arg->SetFile(fdir);
1153 return;
1154 }
1155 }
1156 }
1157 }
1158
1159 filename = arg->fFileName;
1160
1162 if (filename.EndsWith(".gz")) {
1163 filename.Resize(filename.Length() - 3);
1164 iszip = kTRUE;
1165 }
1166
1167 if (IsWSOnly()) {
1168 if (arg->fContent.empty())
1169 arg->Set404();
1170 } else if ((filename == "h.xml") || (filename == "get.xml")) {
1171 Bool_t compact = arg->fQuery.Index("compact") != kNPOS,
1172 processed = kFALSE;
1173 TString res;
1174
1175 res.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1176 if (!compact)
1177 res.Append("\n");
1178 res.Append("<root>");
1179 if (!compact)
1180 res.Append("\n");
1181 {
1182 TRootSnifferStoreXml store(res, compact);
1183 auto len0 = res.Length();
1184 const char *topname = fTopName.Data();
1185 if (arg->fTopName.Length() > 0)
1186 topname = arg->fTopName.Data();
1187 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
1188 processed = res.Length() > len0;
1189 }
1190
1191 res.Append("</root>");
1192 if (!compact)
1193 res.Append("\n");
1194 if (processed) {
1195 arg->SetContent(std::string(res.Data()));
1196 arg->SetXml();
1197 } else
1198 MissedRequest(arg.get());
1199 } else if (filename == "h.json") {
1200 TString res;
1201 TRootSnifferStoreJson store(res, arg->fQuery.Index("compact") != kNPOS);
1202 const char *topname = fTopName.Data();
1203 if (arg->fTopName.Length() > 0)
1204 topname = arg->fTopName.Data();
1205 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1206
1207 if (res.Length() > 0) {
1208 arg->SetContent(std::string(res.Data()));
1209 arg->SetJson();
1210 } else
1211 MissedRequest(arg.get());
1212 } else if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), arg->fContent)) {
1213 // define content type base on extension
1214 arg->SetContentType(GetMimeType(filename.Data()));
1215 } else {
1216 // miss request, user may process
1217 MissedRequest(arg.get());
1218 }
1219
1220 if (arg->Is404())
1221 return;
1222
1223 if (iszip)
1224 arg->SetZipping(THttpCallArg::kZipAlways);
1225
1226 if (filename == "root.bin") {
1227 // only for binary data master version is important
1228 // it allows to detect if streamer info was modified
1229 const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
1230 arg->AddHeader(parname, TString::Format("%u", (unsigned)fSniffer->GetStreamerInfoHash()).Data());
1231 }
1232
1233 // try to avoid caching on the browser
1234 arg->AddNoCacheHeader();
1235
1236 // potentially add cors headers
1237 if (IsCors())
1238 arg->AddHeader("Access-Control-Allow-Origin", GetCors());
1239 if (IsCorsCredentials()) {
1240 arg->AddHeader("Access-Control-Allow-Credentials", GetCorsCredentials());
1241 arg->AddHeader("Access-Control-Expose-Headers", "Content-Range, Content-Length, Date");
1242 arg->AddHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
1243 }
1244}
1245
1246////////////////////////////////////////////////////////////////////////////////
1247/// Register object in folders hierarchy
1248///
1249/// See TRootSniffer::RegisterObject() for more details
1250
1252{
1253 return fSniffer->RegisterObject(subfolder, obj);
1254}
1255
1256////////////////////////////////////////////////////////////////////////////////
1257/// Unregister object in folders hierarchy
1258///
1259/// See TRootSniffer::UnregisterObject() for more details
1260
1262{
1263 return fSniffer->UnregisterObject(obj);
1264}
1265
1266////////////////////////////////////////////////////////////////////////////////
1267/// Register WS handler to the THttpServer
1268///
1269/// Only such handler can be used in multi-threaded processing of websockets
1270
1271void THttpServer::RegisterWS(std::shared_ptr<THttpWSHandler> ws)
1272{
1273 std::lock_guard<std::mutex> grd(fWSMutex);
1274 fWSHandlers.emplace_back(ws);
1275}
1276
1277////////////////////////////////////////////////////////////////////////////////
1278/// Unregister WS handler to the THttpServer
1279
1280void THttpServer::UnregisterWS(std::shared_ptr<THttpWSHandler> ws)
1281{
1282 std::lock_guard<std::mutex> grd(fWSMutex);
1283 for (int n = (int)fWSHandlers.size(); n > 0; --n)
1284 if ((fWSHandlers[n - 1] == ws) || fWSHandlers[n - 1]->IsDisabled())
1285 fWSHandlers.erase(fWSHandlers.begin() + n - 1);
1286}
1287
1288////////////////////////////////////////////////////////////////////////////////
1289/// Search WS handler with given name
1290///
1291/// Handler must be registered with RegisterWS() method
1292
1293std::shared_ptr<THttpWSHandler> THttpServer::FindWS(const char *name)
1294{
1295 std::lock_guard<std::mutex> grd(fWSMutex);
1296 for (auto &ws : fWSHandlers) {
1297 if (strcmp(name, ws->GetName()) == 0)
1298 return ws;
1299 }
1300
1301 return nullptr;
1302}
1303
1304////////////////////////////////////////////////////////////////////////////////
1305/// Execute WS related operation
1306
1308{
1309 if (fTerminated) {
1310 arg->Set404();
1311 return kFALSE;
1312 }
1313
1314 auto wsptr = FindWS(arg->GetPathName());
1315
1316 auto handler = wsptr.get();
1317
1318 if (!handler && !external_thrd)
1319 handler = dynamic_cast<THttpWSHandler *>(fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
1320
1321 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1322
1323 std::unique_lock<std::mutex> lk(fMutex);
1324 fArgs.push(arg);
1325 // and now wait until request is processed
1326 if (wait_process)
1327 arg->fCond.wait(lk);
1328
1329 return kTRUE;
1330 }
1331
1332 if (!handler) {
1333 arg->Set404();
1334 return kFALSE;
1335 }
1336
1337 Bool_t process = kFALSE;
1338
1339 if (arg->fFileName == "root.websocket") {
1340 // handling of web socket
1341 process = handler->HandleWS(arg);
1342 } else if (arg->fFileName == "root.longpoll") {
1343 // ROOT emulation of websocket with polling requests
1344 if (arg->fQuery.BeginsWith("raw_connect") || arg->fQuery.BeginsWith("txt_connect")) {
1345 // try to emulate websocket connect
1346 // if accepted, reply with connection id, which must be used in the following communications
1347 arg->SetMethod("WS_CONNECT");
1348
1349 bool israw = arg->fQuery.BeginsWith("raw_connect");
1350
1351 // automatically assign engine to arg
1352 arg->CreateWSEngine<THttpLongPollEngine>(israw);
1353
1354 if (handler->HandleWS(arg)) {
1355 arg->SetMethod("WS_READY");
1356
1357 if (handler->HandleWS(arg))
1358 arg->SetTextContent(std::string(israw ? "txt:" : "") + std::to_string(arg->GetWSId()));
1359 } else {
1360 arg->TakeWSEngine(); // delete handle
1361 }
1362
1363 process = arg->IsText();
1364 } else {
1365 TUrl url;
1366 url.SetOptions(arg->fQuery);
1367 url.ParseOptions();
1368 const char *connid = url.GetValueFromOptions("connection");
1369 if (connid)
1370 arg->SetWSId(std::stoul(connid));
1371 if (url.HasOption("close")) {
1372 arg->SetMethod("WS_CLOSE");
1373 arg->SetTextContent("OK");
1374 } else {
1375 arg->SetMethod("WS_DATA");
1376 }
1377
1378 process = handler->HandleWS(arg);
1379 }
1380 }
1381
1382 if (!process)
1383 arg->Set404();
1384
1385 return process;
1386}
1387
1388////////////////////////////////////////////////////////////////////////////////
1389/// Restrict access to specified object
1390///
1391/// See TRootSniffer::Restrict() for more details
1392
1393void THttpServer::Restrict(const char *path, const char *options)
1394{
1395 fSniffer->Restrict(path, options);
1396}
1397
1398////////////////////////////////////////////////////////////////////////////////
1399/// Register command which can be executed from web interface
1400///
1401/// As method one typically specifies string, which is executed with
1402/// gROOT->ProcessLine() method. For instance:
1403///
1404/// serv->RegisterCommand("Invoke","InvokeFunction()");
1405///
1406/// Or one could specify any method of the object which is already registered
1407/// to the server. For instance:
1408///
1409/// serv->Register("/", hpx);
1410/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1411///
1412/// Here symbols '/->' separates item name from method to be executed
1413///
1414/// One could specify additional arguments in the command with
1415/// syntax like %arg1%, %arg2% and so on. For example:
1416///
1417/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1418/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1419///
1420/// Such parameter(s) will be requested when command clicked in the browser.
1421///
1422/// Once command is registered, one could specify icon which will appear in the browser:
1423///
1424/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1425///
1426/// One also can set extra property '_fastcmd', that command appear as
1427/// tool button on the top of the browser tree:
1428///
1429/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1430///
1431/// Or it is equivalent to specifying extra argument when register command:
1432///
1433/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1434
1435Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1436{
1437 return fSniffer->RegisterCommand(cmdname, method, icon);
1438}
1439
1440////////////////////////////////////////////////////////////////////////////////
1441/// Hides folder or element from web gui
1442
1444{
1445 return SetItemField(foldername, "_hidden", hide ? "true" : (const char *)0);
1446}
1447
1448////////////////////////////////////////////////////////////////////////////////
1449/// Set name of icon, used in browser together with the item
1450///
1451/// One could use images from $ROOTSYS directory like:
1452/// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
1453
1454Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
1455{
1456 return SetItemField(fullname, "_icon", iconname);
1457}
1458
1459////////////////////////////////////////////////////////////////////////////////
1460/// Create item in sniffer
1461
1462Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
1463{
1464 return fSniffer->CreateItem(fullname, title);
1465}
1466
1467////////////////////////////////////////////////////////////////////////////////
1468/// Set item field in sniffer
1469
1470Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
1471{
1472 return fSniffer->SetItemField(fullname, name, value);
1473}
1474
1475////////////////////////////////////////////////////////////////////////////////
1476/// Get item field from sniffer
1477
1478const char *THttpServer::GetItemField(const char *fullname, const char *name)
1479{
1480 return fSniffer->GetItemField(fullname, name);
1481}
1482
1483////////////////////////////////////////////////////////////////////////////////
1484/// Returns MIME type base on file extension
1485
1486const char *THttpServer::GetMimeType(const char *path)
1487{
1488 static const struct {
1489 const char *extension;
1490 int ext_len;
1491 const char *mime_type;
1492 } builtin_mime_types[] = {{".xml", 4, "text/xml"},
1493 {".json", 5, "application/json"},
1494 {".bin", 4, "application/x-binary"},
1495 {".gif", 4, "image/gif"},
1496 {".jpg", 4, "image/jpeg"},
1497 {".png", 4, "image/png"},
1498 {".html", 5, "text/html"},
1499 {".htm", 4, "text/html"},
1500 {".shtm", 5, "text/html"},
1501 {".shtml", 6, "text/html"},
1502 {".css", 4, "text/css"},
1503 {".js", 3, "application/x-javascript"},
1504 {".mjs", 4, "text/javascript"},
1505 {".ico", 4, "image/x-icon"},
1506 {".jpeg", 5, "image/jpeg"},
1507 {".svg", 4, "image/svg+xml"},
1508 {".txt", 4, "text/plain"},
1509 {".torrent", 8, "application/x-bittorrent"},
1510 {".wav", 4, "audio/x-wav"},
1511 {".mp3", 4, "audio/x-mp3"},
1512 {".mid", 4, "audio/mid"},
1513 {".m3u", 4, "audio/x-mpegurl"},
1514 {".ogg", 4, "application/ogg"},
1515 {".ram", 4, "audio/x-pn-realaudio"},
1516 {".xslt", 5, "application/xml"},
1517 {".xsl", 4, "application/xml"},
1518 {".ra", 3, "audio/x-pn-realaudio"},
1519 {".doc", 4, "application/msword"},
1520 {".exe", 4, "application/octet-stream"},
1521 {".zip", 4, "application/x-zip-compressed"},
1522 {".xls", 4, "application/excel"},
1523 {".tgz", 4, "application/x-tar-gz"},
1524 {".tar", 4, "application/x-tar"},
1525 {".gz", 3, "application/x-gunzip"},
1526 {".arj", 4, "application/x-arj-compressed"},
1527 {".rar", 4, "application/x-arj-compressed"},
1528 {".rtf", 4, "application/rtf"},
1529 {".pdf", 4, "application/pdf"},
1530 {".swf", 4, "application/x-shockwave-flash"},
1531 {".mpg", 4, "video/mpeg"},
1532 {".webm", 5, "video/webm"},
1533 {".mpeg", 5, "video/mpeg"},
1534 {".mov", 4, "video/quicktime"},
1535 {".mp4", 4, "video/mp4"},
1536 {".m4v", 4, "video/x-m4v"},
1537 {".asf", 4, "video/x-ms-asf"},
1538 {".avi", 4, "video/x-msvideo"},
1539 {".bmp", 4, "image/bmp"},
1540 {".ttf", 4, "application/x-font-ttf"},
1541 {".woff", 5, "font/woff"},
1542 {".woff2", 6, "font/woff2"},
1543 {NULL, 0, NULL}};
1544
1545 int path_len = strlen(path);
1546
1547 for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
1549 continue;
1550 const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
1551 if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
1552 return builtin_mime_types[i].mime_type;
1553 }
1554 }
1555
1556 return "text/plain";
1557}
1558
1559////////////////////////////////////////////////////////////////////////////////
1560/// Reads file content
1561///
1562/// @deprecated
1563
1565{
1566 len = 0;
1567
1568 std::ifstream is(filename, std::ios::in | std::ios::binary);
1569 if (!is)
1570 return nullptr;
1571
1572 is.seekg(0, is.end);
1573 len = is.tellg();
1574 is.seekg(0, is.beg);
1575
1576 char *buf = (char *)malloc(len);
1577 is.read(buf, len);
1578 if (!is) {
1579 free(buf);
1580 len = 0;
1581 return nullptr;
1582 }
1583
1584 return buf;
1585}
1586
1587////////////////////////////////////////////////////////////////////////////////
1588/// Reads file content, using std::string as container
1589
1590std::string THttpServer::ReadFileContent(const std::string &filename)
1591{
1592 std::ifstream is(filename, std::ios::in | std::ios::binary);
1593 std::string res;
1594 if (is) {
1595 is.seekg(0, std::ios::end);
1596 res.resize(is.tellg());
1597 is.seekg(0, std::ios::beg);
1598 is.read((char *)res.data(), res.length());
1599 if (!is)
1600 res.clear();
1601 }
1602 return res;
1603}
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:126
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:148
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:777
#define gROOT
Definition TROOT.h:417
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 @137 builtin_mime_types[]
#define malloc
Definition civetweb.c:1575
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:2994
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
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:1084
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1098
static const TString & GetRootSys()
Get the rootsys directory in the installation. Static utility function.
Definition TROOT.cxx:3140
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Definition TROOT.cxx:3391
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