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