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