Logo ROOT   6.08/07
Reference Guide
THttpServer.cxx
Go to the documentation of this file.
1 // $Id$
2 // Author: Sergey Linev 21/12/2013
3 
4 #include "THttpServer.h"
5 
6 #include "TThread.h"
7 #include "TTimer.h"
8 #include "TSystem.h"
9 #include "TImage.h"
10 #include "TROOT.h"
11 #include "TClass.h"
12 #include "TCanvas.h"
13 #include "TFolder.h"
14 #include "RVersion.h"
15 #include "RConfigure.h"
16 
17 #include "THttpEngine.h"
18 #include "TRootSniffer.h"
19 #include "TRootSnifferStore.h"
20 
21 #include <string>
22 #include <cstdlib>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <fstream>
26 
27 
28 //////////////////////////////////////////////////////////////////////////
29 // //
30 // THttpTimer //
31 // //
32 // Specialized timer for THttpServer //
33 // Provides regular call of THttpServer::ProcessRequests() method //
34 // //
35 //////////////////////////////////////////////////////////////////////////
36 
37 
38 ////////////////////////////////////////////////////////////////////////////////
39 
40 class THttpTimer : public TTimer {
41 public:
42 
43  THttpServer *fServer; //!
44 
45  THttpTimer(Long_t milliSec, Bool_t mode, THttpServer *serv) :
46  TTimer(milliSec, mode), fServer(serv)
47  {
48  // construtor
49  }
50  virtual ~THttpTimer()
51  {
52  // destructor
53  }
54  virtual void Timeout()
55  {
56  // timeout handler
57  // used to process http requests in main ROOT thread
58 
59  if (fServer) fServer->ProcessRequests();
60  }
61 };
62 
63 // =======================================================
64 
65 //////////////////////////////////////////////////////////////////////////
66 // //
67 // THttpServer //
68 // //
69 // Online http server for arbitrary ROOT application //
70 // //
71 // Idea of THttpServer - provide remote http access to running //
72 // ROOT application and enable HTML/JavaScript user interface. //
73 // Any registered object can be requested and displayed in the browser. //
74 // There are many benefits of such approach: //
75 // * standard http interface to ROOT application //
76 // * no any temporary ROOT files when access data //
77 // * user interface running in all browsers //
78 // //
79 // Starting HTTP server //
80 // //
81 // To start http server, at any time create instance //
82 // of the THttpServer class like: //
83 // serv = new THttpServer("http:8080"); //
84 // //
85 // This will starts civetweb-based http server with http port 8080. //
86 // Than one should be able to open address "http://localhost:8080" //
87 // in any modern browser (IE, Firefox, Chrome) and browse objects, //
88 // created in application. By default, server can access files, //
89 // canvases and histograms via gROOT pointer. All such objects //
90 // can be displayed with JSROOT graphics. //
91 // //
92 // At any time one could register other objects with the command: //
93 // //
94 // TGraph* gr = new TGraph(10); //
95 // gr->SetName("gr1"); //
96 // serv->Register("graphs/subfolder", gr); //
97 // //
98 // If objects content is changing in the application, one could //
99 // enable monitoring flag in the browser - than objects view //
100 // will be regularly updated. //
101 // //
102 // More information: http://root.cern.ch/drupal/content/users-guide //
103 // //
104 //////////////////////////////////////////////////////////////////////////
105 
107 
108 ////////////////////////////////////////////////////////////////////////////////
109 /// constructor
110 
111 THttpServer::THttpServer(const char *engine) :
112  TNamed("http", "ROOT http server"),
113  fEngines(),
114  fTimer(0),
115  fSniffer(0),
116  fMainThrdId(0),
117  fJSROOTSYS(),
118  fTopName("ROOT"),
119  fJSROOT(),
120  fLocations(),
121  fDefaultPage(),
122  fDefaultPageCont(),
123  fDrawPage(),
124  fDrawPageCont(),
125  fCallArgs()
126 {
127  // As argument, one specifies engine kind which should be
128  // created like "http:8080". One could specify several engines
129  // at once, separating them with ; like "http:8080;fastcgi:9000"
130  // One also can configure readonly flag for sniffer like
131  // "http:8080;readonly" or "http:8080;readwrite"
132  //
133  // Also searches for JavaScript ROOT sources, which are used in web clients
134  // Typically JSROOT sources located in $ROOTSYS/etc/http directory,
135  // but one could set JSROOTSYS variable to specify alternative location
136 
137  fLocations.SetOwner(kTRUE);
138 
139  // Info("THttpServer", "Create %p in thrd %ld", this, (long) fMainThrdId);
140 
141 #ifdef COMPILED_WITH_DABC
142  const char *dabcsys = gSystem->Getenv("DABCSYS");
143  if (dabcsys != 0)
144  fJSROOTSYS = TString::Format("%s/plugins/root/js", dabcsys);
145 #endif
146 
147  const char *jsrootsys = gSystem->Getenv("JSROOTSYS");
148  if (jsrootsys != 0) fJSROOTSYS = jsrootsys;
149 
150  if (fJSROOTSYS.Length() == 0) {
151 #ifdef ROOTETCDIR
152  TString jsdir = TString::Format("%s/http", ROOTETCDIR);
153 #else
154  TString jsdir("$(ROOTSYS)/etc/http");
155 #endif
156  if (gSystem->ExpandPathName(jsdir)) {
157  Warning("THttpServer", "problems resolving '%s', use JSROOTSYS to specify $ROOTSYS/etc/http location", jsdir.Data());
158  fJSROOTSYS = ".";
159  } else {
160  fJSROOTSYS = jsdir;
161  }
162  }
163 
164  AddLocation("currentdir/", ".");
165  AddLocation("jsrootsys/", fJSROOTSYS);
166 
167  const char *rootsys = gSystem->Getenv("ROOTSYS");
168  if (rootsys != 0) {
169  AddLocation("rootsys/", rootsys);
170  } else {
171 #ifdef ROOTPREFIX
172  TString sysdir = ROOTPREFIX;
173 #else
174  TString sysdir = "$(ROOTSYS)";
175 #endif
176  if (!gSystem->ExpandPathName(sysdir)) AddLocation("rootsys/", sysdir);
177  }
178 
179  fDefaultPage = fJSROOTSYS + "/files/online.htm";
180  fDrawPage = fJSROOTSYS + "/files/draw.htm";
181 
182  SetSniffer(new TRootSniffer("sniff"));
183 
184  // start timer
185  SetTimer(20, kTRUE);
186 
187  if (strchr(engine, ';') == 0) {
188  CreateEngine(engine);
189  } else {
190  TObjArray *lst = TString(engine).Tokenize(";");
191 
192  for (Int_t n = 0; n <= lst->GetLast(); n++) {
193  const char *opt = lst->At(n)->GetName();
194  if ((strcmp(opt, "readonly") == 0) || (strcmp(opt, "ro") == 0)) {
195  GetSniffer()->SetReadOnly(kTRUE);
196  } else if ((strcmp(opt, "readwrite") == 0) || (strcmp(opt, "rw") == 0)) {
197  GetSniffer()->SetReadOnly(kFALSE);
198  } else
199  CreateEngine(opt);
200  }
201 
202  delete lst;
203  }
204 }
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 /// destructor
208 /// delete all http engines and sniffer
209 
211 {
212  fEngines.Delete();
213 
214  SetSniffer(0);
215 
216  SetTimer(0);
217 }
218 
219 ////////////////////////////////////////////////////////////////////////////////
220 /// Set TRootSniffer to the server
221 /// Server takes ownership over sniffer
222 
224 {
225  if (fSniffer) delete fSniffer;
226  fSniffer = sniff;
227 }
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 /// returns read-only mode
231 
233 {
234  return fSniffer ? fSniffer->IsReadOnly() : kTRUE;
235 }
236 
237 ////////////////////////////////////////////////////////////////////////////////
238 /// Set read-only mode for the server (default on)
239 /// In read-only server is not allowed to change any ROOT object, registered to the server
240 /// Server also cannot execute objects method via exe.json request
241 
243 {
244  if (fSniffer) fSniffer->SetReadOnly(readonly);
245 }
246 
247 ////////////////////////////////////////////////////////////////////////////////
248 /// add files location, which could be used in the server
249 /// one could map some system folder to the server like AddLocation("mydir/","/home/user/specials");
250 /// Than files from this directory could be addressed via server like
251 /// http://localhost:8080/mydir/myfile.root
252 
253 void THttpServer::AddLocation(const char *prefix, const char *path)
254 {
255  if ((prefix==0) || (*prefix==0)) return;
256 
257  TNamed *obj = dynamic_cast<TNamed*> (fLocations.FindObject(prefix));
258  if (obj != 0) {
259  obj->SetTitle(path);
260  } else {
261  fLocations.Add(new TNamed(prefix, path));
262  }
263 }
264 
265 ////////////////////////////////////////////////////////////////////////////////
266 /// Set location of JSROOT to use with the server
267 /// One could specify address like:
268 /// https://root.cern.ch/js/3.3/
269 /// http://web-docs.gsi.de/~linev/js/3.3/
270 /// This allows to get new JSROOT features with old server,
271 /// reduce load on THttpServer instance, also startup time can be improved
272 /// When empty string specified (default), local copy of JSROOT is used (distributed with ROOT)
273 
274 void THttpServer::SetJSROOT(const char* location)
275 {
276  fJSROOT = location ? location : "";
277 }
278 
279 ////////////////////////////////////////////////////////////////////////////////
280 /// Set file name of HTML page, delivered by the server when
281 /// http address is opened in the browser.
282 /// By default, $ROOTSYS/etc/http/files/online.htm page is used
283 /// When empty filename is specified, default page will be used
284 
285 void THttpServer::SetDefaultPage(const char* filename)
286 {
287  if ((filename!=0) && (*filename!=0))
288  fDefaultPage = filename;
289  else
290  fDefaultPage = fJSROOTSYS + "/files/online.htm";
291 
292  // force to read page content next time again
293  fDefaultPageCont.Clear();
294 }
295 
296 ////////////////////////////////////////////////////////////////////////////////
297 /// Set file name of HTML page, delivered by the server when
298 /// objects drawing page is requested from the browser
299 /// By default, $ROOTSYS/etc/http/files/draw.htm page is used
300 /// When empty filename is specified, default page will be used
301 
302 void THttpServer::SetDrawPage(const char* filename)
303 {
304  if ((filename!=0) && (*filename!=0))
305  fDrawPage = filename;
306  else
307  fDrawPage = fJSROOTSYS + "/files/draw.htm";
308 
309  // force to read page content next time again
310  fDrawPageCont.Clear();
311 }
312 
313 ////////////////////////////////////////////////////////////////////////////////
314 /// factory method to create different http engines
315 /// At the moment two engine kinds are supported:
316 /// civetweb (default) and fastcgi
317 /// Examples:
318 /// "civetweb:8080" or "http:8080" or ":8080" - creates civetweb web server with http port 8080
319 /// "fastcgi:9000" - creates fastcgi server with port 9000
320 /// "dabc:1237" - create DABC server with port 1237 (only available with DABC installed)
321 /// "dabc:master_host:port" - attach to DABC master, running on master_host:port (only available with DABC installed)
322 
324 {
325  if (engine == 0) return kFALSE;
326 
327  const char *arg = strchr(engine, ':');
328  if (arg == 0) return kFALSE;
329 
330  TString clname;
331  if (arg != engine) clname.Append(engine, arg - engine);
332 
333  if ((clname.Length() == 0) || (clname == "http") || (clname == "civetweb"))
334  clname = "TCivetweb";
335  else if (clname == "fastcgi")
336  clname = "TFastCgi";
337  else if (clname == "dabc")
338  clname = "TDabcEngine";
339 
340  // ensure that required engine class exists before we try to create it
341  TClass *engine_class = gROOT->LoadClass(clname.Data());
342  if (engine_class == 0) return kFALSE;
343 
344  THttpEngine *eng = (THttpEngine *) engine_class->New();
345  if (eng == 0) return kFALSE;
346 
347  eng->SetServer(this);
348 
349  if (!eng->Create(arg + 1)) {
350  delete eng;
351  return kFALSE;
352  }
353 
354  fEngines.Add(eng);
355 
356  return kTRUE;
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////////
360 /// create timer which will invoke ProcessRequests() function periodically
361 /// Timer is required to perform all actions in main ROOT thread
362 /// Method arguments are the same as for TTimer constructor
363 /// By default, sync timer with 100 ms period is created
364 ///
365 /// If milliSec == 0, no timer will be created.
366 /// In this case application should regularly call ProcessRequests() method.
367 
368 void THttpServer::SetTimer(Long_t milliSec, Bool_t mode)
369 {
370  if (fTimer) {
371  fTimer->Stop();
372  delete fTimer;
373  fTimer = 0;
374  }
375  if (milliSec > 0) {
376  fTimer = new THttpTimer(milliSec, mode, this);
377  fTimer->TurnOn();
378  }
379 }
380 
381 ////////////////////////////////////////////////////////////////////////////////
382 /// Checked that filename does not contains relative path below current directory
383 /// Used to prevent access to files below current directory
384 
386 {
387  if ((fname == 0) || (*fname == 0)) return kFALSE;
388 
389  Int_t level = 0;
390 
391  while (*fname != 0) {
392 
393  // find next slash or backslash
394  const char *next = strpbrk(fname, "/\\");
395  if (next == 0) return kTRUE;
396 
397  // most important - change to parent dir
398  if ((next == fname + 2) && (*fname == '.') && (*(fname + 1) == '.')) {
399  fname += 3;
400  level--;
401  if (level < 0) return kFALSE;
402  continue;
403  }
404 
405  // ignore current directory
406  if ((next == fname + 1) && (*fname == '.')) {
407  fname += 2;
408  continue;
409  }
410 
411  // ignore slash at the front
412  if (next == fname) {
413  fname ++;
414  continue;
415  }
416 
417  fname = next + 1;
418  level++;
419  }
420 
421  return kTRUE;
422 }
423 
424 ////////////////////////////////////////////////////////////////////////////////
425 /// Verifies that request is just file name
426 /// File names typically contains prefix like "jsrootsys/"
427 /// If true, method returns real name of the file,
428 /// which should be delivered to the client
429 /// Method is thread safe and can be called from any thread
430 
431 Bool_t THttpServer::IsFileRequested(const char *uri, TString &res) const
432 {
433  if ((uri == 0) || (strlen(uri) == 0)) return kFALSE;
434 
435  TString fname = uri;
436 
437  TIter iter(&fLocations);
438  TObject *obj(0);
439  while ((obj=iter()) != 0) {
440  Ssiz_t pos = fname.Index(obj->GetName());
441  if (pos == kNPOS) continue;
442  fname.Remove(0, pos + (strlen(obj->GetName()) - 1));
443  if (!VerifyFilePath(fname.Data())) return kFALSE;
444  res = obj->GetTitle();
445  if ((fname[0]=='/') && (res[res.Length()-1]=='/')) res.Resize(res.Length()-1);
446  res.Append(fname);
447  return kTRUE;
448  }
449 
450  return kFALSE;
451 }
452 
453 ////////////////////////////////////////////////////////////////////////////////
454 /// Executes http request, specified in THttpCallArg structure
455 /// Method can be called from any thread
456 /// Actual execution will be done in main ROOT thread, where analysis code is running.
457 
459 {
460  if ((fMainThrdId!=0) && (fMainThrdId == TThread::SelfId())) {
461  // should not happen, but one could process requests directly without any signaling
462 
463  ProcessRequest(arg);
464 
465  return kTRUE;
466  }
467 
468  // add call arg to the list
469  std::unique_lock<std::mutex> lk(fMutex);
470  fCallArgs.Add(arg);
471  // and now wait until request is processed
472  arg->fCond.wait(lk);
473 
474  return kTRUE;
475 }
476 
477 ////////////////////////////////////////////////////////////////////////////////
478 /// Process requests, submitted for execution
479 /// Regularly invoked by THttpTimer, when somewhere in the code
480 /// gSystem->ProcessEvents() is called.
481 /// User can call serv->ProcessRequests() directly, but only from main analysis thread.
482 
484 {
485  if (fMainThrdId==0) fMainThrdId = TThread::SelfId();
486 
487  if (fMainThrdId != TThread::SelfId()) {
488  Error("ProcessRequests", "Should be called only from main ROOT thread");
489  return;
490  }
491 
492  std::unique_lock<std::mutex> lk(fMutex, std::defer_lock);
493  while (true) {
494  THttpCallArg *arg = 0;
495 
496  lk.lock();
497  if (fCallArgs.GetSize() > 0) {
498  arg = (THttpCallArg *) fCallArgs.First();
499  fCallArgs.RemoveFirst();
500  }
501  lk.unlock();
502 
503  if (arg == 0) break;
504 
505  fSniffer->SetCurrentCallArg(arg);
506 
507  try {
508  ProcessRequest(arg);
509  fSniffer->SetCurrentCallArg(0);
510  } catch (...) {
511  fSniffer->SetCurrentCallArg(0);
512  }
513 
514  arg->fCond.notify_one();
515  }
516 
517  // regularly call Process() method of engine to let perform actions in ROOT context
518  TIter iter(&fEngines);
519  THttpEngine *engine = 0;
520  while ((engine = (THttpEngine *)iter()) != 0)
521  engine->Process();
522 }
523 
524 ////////////////////////////////////////////////////////////////////////////////
525 /// Process single http request
526 /// Depending from requested path and filename different actions will be performed.
527 /// In most cases information is provided by TRootSniffer class
528 
530 {
531 
532  if (arg->fFileName.IsNull() || (arg->fFileName == "index.htm")) {
533 
534  if (fDefaultPageCont.Length() == 0) {
535  Int_t len = 0;
536  char *buf = ReadFileContent(fDefaultPage.Data(), len);
537  if (len > 0) fDefaultPageCont.Append(buf, len);
538  delete buf;
539  }
540 
541  if (fDefaultPageCont.Length() == 0) {
542  arg->Set404();
543  } else {
544  arg->fContent = fDefaultPageCont;
545 
546  // replace all references on JSROOT
547  if (fJSROOT.Length() > 0) {
548  TString repl = TString("=\"") + fJSROOT;
549  if (!repl.EndsWith("/")) repl+="/";
550  arg->fContent.ReplaceAll("=\"jsrootsys/", repl);
551  }
552 
553  const char *hjsontag = "\"$$$h.json$$$\"";
554 
555  // add h.json caching
556  if (arg->fContent.Index(hjsontag) != kNPOS) {
557  TString h_json;
558  TRootSnifferStoreJson store(h_json, kTRUE);
559  const char *topname = fTopName.Data();
560  if (arg->fTopName.Length() > 0) topname = arg->fTopName.Data();
561  fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
562 
563  arg->fContent.ReplaceAll(hjsontag, h_json);
564 
565  arg->AddHeader("Cache-Control", "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
566  if (arg->fQuery.Index("nozip") == kNPOS) arg->SetZipping(2);
567  }
568  arg->SetContentType("text/html");
569  }
570  return;
571  }
572 
573  if (arg->fFileName == "draw.htm") {
574  if (fDrawPageCont.Length() == 0) {
575  Int_t len = 0;
576  char *buf = ReadFileContent(fDrawPage.Data(), len);
577  if (len > 0) fDrawPageCont.Append(buf, len);
578  delete buf;
579  }
580 
581  if (fDrawPageCont.Length() == 0) {
582  arg->Set404();
583  } else {
584  const char *rootjsontag = "\"$$$root.json$$$\"";
585  const char *hjsontag = "\"$$$h.json$$$\"";
586 
587  arg->fContent = fDrawPageCont;
588 
589  // replace all references on JSROOT
590  if (fJSROOT.Length() > 0) {
591  TString repl = TString("=\"") + fJSROOT;
592  if (!repl.EndsWith("/")) repl+="/";
593  arg->fContent.ReplaceAll("=\"jsrootsys/", repl);
594  }
595 
596  if (arg->fContent.Index(hjsontag) != kNPOS) {
597  TString h_json;
598  TRootSnifferStoreJson store(h_json, kTRUE);
599  const char *topname = fTopName.Data();
600  if (arg->fTopName.Length() > 0) topname = arg->fTopName.Data();
601  fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, kTRUE);
602 
603  arg->fContent.ReplaceAll(hjsontag, h_json);
604  }
605 
606  if (arg->fContent.Index(rootjsontag) != kNPOS) {
607  TString str;
608  void *bindata = 0;
609  Long_t bindatalen = 0;
610  if (fSniffer->Produce(arg->fPathName.Data(), "root.json", "compact=3", bindata, bindatalen, str)) {
611  arg->fContent.ReplaceAll(rootjsontag, str);
612  }
613  }
614  arg->AddHeader("Cache-Control", "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
615  if (arg->fQuery.Index("nozip") == kNPOS) arg->SetZipping(2);
616  arg->SetContentType("text/html");
617  }
618  return;
619  }
620 
621  TString filename;
622  if (IsFileRequested(arg->fFileName.Data(), filename)) {
623  arg->SetFile(filename);
624  return;
625  }
626 
627  filename = arg->fFileName;
628  Bool_t iszip = kFALSE;
629  if (filename.EndsWith(".gz")) {
630  filename.Resize(filename.Length() - 3);
631  iszip = kTRUE;
632  }
633 
634  void* bindata(0);
635  Long_t bindatalen(0);
636 
637  if ((filename == "h.xml") || (filename == "get.xml")) {
638 
639  Bool_t compact = arg->fQuery.Index("compact") != kNPOS;
640 
641  arg->fContent.Form("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
642  if (!compact) arg->fContent.Append("\n");
643  arg->fContent.Append("<root>");
644  if (!compact) arg->fContent.Append("\n");
645  {
646  TRootSnifferStoreXml store(arg->fContent, compact);
647 
648  const char *topname = fTopName.Data();
649  if (arg->fTopName.Length() > 0) topname = arg->fTopName.Data();
650  fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename == "get.xml");
651  }
652 
653  arg->fContent.Append("</root>");
654  if (!compact) arg->fContent.Append("\n");
655 
656  arg->SetXml();
657  } else
658 
659  if (filename == "h.json") {
660  TRootSnifferStoreJson store(arg->fContent, arg->fQuery.Index("compact") != kNPOS);
661  const char *topname = fTopName.Data();
662  if (arg->fTopName.Length() > 0) topname = arg->fTopName.Data();
663  fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
664  arg->SetJson();
665  } else
666 
667  if (filename == "root.websocket") {
668  // handling of web socket
669 
670  TCanvas* canv = dynamic_cast<TCanvas*> (fSniffer->FindTObjectInHierarchy(arg->fPathName.Data()));
671 
672  if (canv == 0) {
673  // for the moment only TCanvas is used for web sockets
674  arg->Set404();
675  } else
676  if (strcmp(arg->GetMethod(),"WS_CONNECT")==0) {
677 
678  if (canv->GetPrimitive("websocket")) {
679  // websocket already exists, ignore all other requests
680  arg->Set404();
681  }
682  } else
683  if (strcmp(arg->GetMethod(),"WS_READY")==0) {
684  THttpWSEngine* wshandle = dynamic_cast<THttpWSEngine*> (arg->TakeWSHandle());
685 
686  if (gDebug>0) Info("ProcessRequest","Set WebSocket handle %p", wshandle);
687 
688  if (wshandle) wshandle->AssignCanvas(canv);
689 
690  // connection is established
691  } else
692  if (strcmp(arg->GetMethod(),"WS_DATA")==0) {
693  // process received data
694 
695  THttpWSEngine* wshandle = dynamic_cast<THttpWSEngine*> (canv->GetPrimitive("websocket"));
696  if (wshandle) wshandle->ProcessData(arg);
697 
698  } else
699  if (strcmp(arg->GetMethod(),"WS_CLOSE")==0) {
700  // connection is closed, one can remove handle
701 
702  THttpWSEngine* wshandle = dynamic_cast<THttpWSEngine*> (canv->GetPrimitive("websocket"));
703 
704  if (wshandle) {
705  if (gDebug>0) Info("ProcessRequest","Clear WebSocket handle");
706  wshandle->ClearHandle();
707  wshandle->AssignCanvas(0);
708  delete wshandle;
709  }
710  } else {
711  arg->Set404();
712  }
713 
714  return;
715 
716  } else
717 
718  if (fSniffer->Produce(arg->fPathName.Data(), filename.Data(), arg->fQuery.Data(), bindata, bindatalen, arg->fContent)) {
719  if (bindata != 0) arg->SetBinData(bindata, bindatalen);
720 
721  // define content type base on extension
722  arg->SetContentType(GetMimeType(filename.Data()));
723  } else {
724  // request is not processed
725  arg->Set404();
726  }
727 
728  if (arg->Is404()) return;
729 
730  if (iszip) arg->SetZipping(3);
731 
732  if (filename == "root.bin") {
733  // only for binary data master version is important
734  // it allows to detect if streamer info was modified
735  const char *parname = fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ? "BVersion" : "MVersion";
736  arg->AddHeader(parname, Form("%u", (unsigned) fSniffer->GetStreamerInfoHash()));
737  }
738 
739  // try to avoid caching on the browser
740  arg->AddHeader("Cache-Control", "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
741 }
742 
743 ////////////////////////////////////////////////////////////////////////////////
744 /// Register object in folders hierarchy
745 ///
746 /// See TRootSniffer::RegisterObject() for more details
747 
748 Bool_t THttpServer::Register(const char *subfolder, TObject *obj)
749 {
750  return fSniffer->RegisterObject(subfolder, obj);
751 }
752 
753 ////////////////////////////////////////////////////////////////////////////////
754 /// Unregister object in folders hierarchy
755 ///
756 /// See TRootSniffer::UnregisterObject() for more details
757 
759 {
760  return fSniffer->UnregisterObject(obj);
761 }
762 
763 ////////////////////////////////////////////////////////////////////////////////
764 /// Restrict access to specified object
765 ///
766 /// See TRootSniffer::Restrict() for more details
767 
768 void THttpServer::Restrict(const char *path, const char* options)
769 {
770  fSniffer->Restrict(path, options);
771 }
772 
773 ////////////////////////////////////////////////////////////////////////////////
774 /// Register command which can be executed from web interface
775 ///
776 /// As method one typically specifies string, which is executed with
777 /// gROOT->ProcessLine() method. For instance
778 /// serv->RegisterCommand("Invoke","InvokeFunction()");
779 ///
780 /// Or one could specify any method of the object which is already registered
781 /// to the server. For instance:
782 /// serv->Register("/", hpx);
783 /// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
784 /// Here symbols '/->' separates item name from method to be executed
785 ///
786 /// One could specify additional arguments in the command with
787 /// syntax like %arg1%, %arg2% and so on. For example:
788 /// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
789 /// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
790 /// Such parameter(s) will be requested when command clicked in the browser.
791 ///
792 /// Once command is registered, one could specify icon which will appear in the browser:
793 /// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
794 ///
795 /// One also can set extra property '_fastcmd', that command appear as
796 /// tool button on the top of the browser tree:
797 /// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
798 /// Or it is equivalent to specifying extra argument when register command:
799 /// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
800 
801 Bool_t THttpServer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
802 {
803  return fSniffer->RegisterCommand(cmdname, method, icon);
804 }
805 
806 ////////////////////////////////////////////////////////////////////////////////
807 /// hides folder or element from web gui
808 
809 Bool_t THttpServer::Hide(const char *foldername, Bool_t hide)
810 {
811  return SetItemField(foldername, "_hidden", hide ? "true" : (const char *) 0);
812 }
813 
814 ////////////////////////////////////////////////////////////////////////////////
815 /// set name of icon, used in browser together with the item
816 ///
817 /// One could use images from $ROOTSYS directory like:
818 /// serv->SetIcon("/ResetHPX","/rootsys/icons/ed_execute.png");
819 
820 Bool_t THttpServer::SetIcon(const char *fullname, const char *iconname)
821 {
822  return SetItemField(fullname, "_icon", iconname);
823 }
824 
825 ////////////////////////////////////////////////////////////////////////////////
826 
827 Bool_t THttpServer::CreateItem(const char *fullname, const char *title)
828 {
829  return fSniffer->CreateItem(fullname, title);
830 }
831 
832 ////////////////////////////////////////////////////////////////////////////////
833 
834 Bool_t THttpServer::SetItemField(const char *fullname, const char *name, const char *value)
835 {
836  return fSniffer->SetItemField(fullname, name, value);
837 }
838 
839 ////////////////////////////////////////////////////////////////////////////////
840 
841 const char *THttpServer::GetItemField(const char *fullname, const char *name)
842 {
843  return fSniffer->GetItemField(fullname, name);
844 }
845 
846 ////////////////////////////////////////////////////////////////////////////////
847 /// Returns MIME type base on file extension
848 
849 const char *THttpServer::GetMimeType(const char *path)
850 {
851  static const struct {
852  const char *extension;
853  int ext_len;
854  const char *mime_type;
855  } builtin_mime_types[] = {
856  {".xml", 4, "text/xml"},
857  {".json", 5, "application/json"},
858  {".bin", 4, "application/x-binary"},
859  {".gif", 4, "image/gif"},
860  {".jpg", 4, "image/jpeg"},
861  {".png", 4, "image/png"},
862  {".html", 5, "text/html"},
863  {".htm", 4, "text/html"},
864  {".shtm", 5, "text/html"},
865  {".shtml", 6, "text/html"},
866  {".css", 4, "text/css"},
867  {".js", 3, "application/x-javascript"},
868  {".ico", 4, "image/x-icon"},
869  {".jpeg", 5, "image/jpeg"},
870  {".svg", 4, "image/svg+xml"},
871  {".txt", 4, "text/plain"},
872  {".torrent", 8, "application/x-bittorrent"},
873  {".wav", 4, "audio/x-wav"},
874  {".mp3", 4, "audio/x-mp3"},
875  {".mid", 4, "audio/mid"},
876  {".m3u", 4, "audio/x-mpegurl"},
877  {".ogg", 4, "application/ogg"},
878  {".ram", 4, "audio/x-pn-realaudio"},
879  {".xslt", 5, "application/xml"},
880  {".xsl", 4, "application/xml"},
881  {".ra", 3, "audio/x-pn-realaudio"},
882  {".doc", 4, "application/msword"},
883  {".exe", 4, "application/octet-stream"},
884  {".zip", 4, "application/x-zip-compressed"},
885  {".xls", 4, "application/excel"},
886  {".tgz", 4, "application/x-tar-gz"},
887  {".tar", 4, "application/x-tar"},
888  {".gz", 3, "application/x-gunzip"},
889  {".arj", 4, "application/x-arj-compressed"},
890  {".rar", 4, "application/x-arj-compressed"},
891  {".rtf", 4, "application/rtf"},
892  {".pdf", 4, "application/pdf"},
893  {".swf", 4, "application/x-shockwave-flash"},
894  {".mpg", 4, "video/mpeg"},
895  {".webm", 5, "video/webm"},
896  {".mpeg", 5, "video/mpeg"},
897  {".mov", 4, "video/quicktime"},
898  {".mp4", 4, "video/mp4"},
899  {".m4v", 4, "video/x-m4v"},
900  {".asf", 4, "video/x-ms-asf"},
901  {".avi", 4, "video/x-msvideo"},
902  {".bmp", 4, "image/bmp"},
903  {".ttf", 4, "application/x-font-ttf"},
904  {NULL, 0, NULL}
905  };
906 
907  int path_len = strlen(path);
908 
909  for (int i = 0; builtin_mime_types[i].extension != NULL; i++) {
910  if (path_len <= builtin_mime_types[i].ext_len) continue;
911  const char *ext = path + (path_len - builtin_mime_types[i].ext_len);
912  if (strcmp(ext, builtin_mime_types[i].extension) == 0) {
913  return builtin_mime_types[i].mime_type;
914  }
915  }
916 
917  return "text/plain";
918 }
919 
920 ////////////////////////////////////////////////////////////////////////////////
921 /// reads file content
922 
923 char *THttpServer::ReadFileContent(const char *filename, Int_t &len)
924 {
925  len = 0;
926 
927  std::ifstream is(filename);
928  if (!is) return 0;
929 
930  is.seekg(0, is.end);
931  len = is.tellg();
932  is.seekg(0, is.beg);
933 
934  char *buf = (char *) malloc(len);
935  is.read(buf, len);
936  if (!is) {
937  free(buf);
938  len = 0;
939  return 0;
940  }
941 
942  return buf;
943 }
void SetZipping(Int_t kind)
Definition: THttpCallArg.h:277
An array of TObjects.
Definition: TObjArray.h:39
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:899
virtual void Process()
Method regularly called in main ROOT context.
Definition: THttpEngine.h:30
This namespace contains pre-defined functions to be used in conjuction with TExecutor::Map and TExecu...
Definition: StringConv.hxx:21
const char * mime_type
Definition: civetweb.c:5007
Storage of hierarchy scan in TRootSniffer in JSON format.
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:635
const char * GetItemField(const char *fullname, const char *name)
Bool_t IsReadOnly() const
returns read-only mode
virtual void ProcessData(THttpCallArg *arg)
process data received from the client
#define gROOT
Definition: TROOT.h:364
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:582
TString fQuery
authenticated user name (if any)
Definition: THttpCallArg.h:30
Basic string class.
Definition: TString.h:137
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kFALSE
Definition: Rtypes.h:92
TObject * At(Int_t idx) const
Definition: TObjArray.h:167
void SetServer(THttpServer *serv)
Definition: THttpEngine.h:24
virtual void AssignCanvas(TCanvas *canv)
assign canvas to the web socket connects with CanvasModified signal
#define malloc
Definition: civetweb.c:818
void SetContentType(const char *typ)
Definition: THttpCallArg.h:207
void ProcessRequests()
Process submitted requests, must be called from main thread.
Bool_t CreateItem(const char *fullname, const char *title)
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:2335
The TNamed class is the base class for all named ROOT classes.
Definition: TNamed.h:33
virtual const char * Getenv(const char *env)
Get environment variable.
Definition: TSystem.cxx:1628
TString fPathName
request method like GET or POST
Definition: THttpCallArg.h:27
TString & Append(const char *cs)
Definition: TString.h:492
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition: TString.cxx:2221
static Long_t SelfId()
Static method returning the id for the current thread.
Definition: TThread.cxx:538
void SetTimer(Long_t milliSec=100, Bool_t mode=kTRUE)
create timer which will invoke ProcessRequests() function periodically Timer is required to perform a...
Storage of hierarchy scan in TRootSniffer in XML format.
Bool_t Register(const char *subfolder, TObject *obj)
Register object in subfolder.
void SetReadOnly(Bool_t readonly)
Set read-only mode for the server (default on) In read-only server is not allowed to change any ROOT ...
TString fTopName
Definition: THttpCallArg.h:25
Int_t GetLast() const
Return index of last object in array.
Definition: TObjArray.cxx:528
void SetBinData(void *data, Long_t length)
set binary data, which will be returned as reply body
void SetJson()
Definition: THttpCallArg.h:236
Bool_t CreateEngine(const char *engine)
factory method to create different http engines At the moment two engine kinds are supported: civetwe...
size_t ext_len
Definition: civetweb.c:5006
R__EXTERN TSystem * gSystem
Definition: TSystem.h:549
virtual TObject * GetPrimitive(const char *name) const
Get primitive.
Definition: TPad.cxx:2770
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2322
virtual ~THttpServer()
destructor delete all http engines and sniffer
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:925
char * Form(const char *fmt,...)
Ssiz_t Length() const
Definition: TString.h:390
virtual Bool_t Create(const char *)
Method to create all components of engine.
Definition: THttpEngine.h:36
Handles synchronous and a-synchronous timer events.
Definition: TTimer.h:57
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:81
void SetSniffer(TRootSniffer *sniff)
Set TRootSniffer to the server Server takes ownership over sniffer.
static const struct @165 builtin_mime_types[]
Bool_t ExecuteHttp(THttpCallArg *arg)
Execute HTTP request.
const char * extension
Definition: civetweb.c:5005
TString & Remove(Ssiz_t pos)
Definition: TString.h:616
long Long_t
Definition: RtypesCore.h:50
int Ssiz_t
Definition: RtypesCore.h:63
std::condition_variable fCond
web-socket handle, derived from TNamed class
Definition: THttpCallArg.h:37
The Canvas class.
Definition: TCanvas.h:41
void AddLocation(const char *prefix, const char *path)
add files location, which could be used in the server one could map some system folder to the server ...
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition: TString.cxx:2241
Bool_t Hide(const char *fullname, Bool_t hide=kTRUE)
hides folder or element from web gui
#define ClassImp(name)
Definition: Rtypes.h:279
virtual void ProcessRequest(THttpCallArg *arg)
submitted arguments
#define free
Definition: civetweb.c:821
TNamed * TakeWSHandle()
takeout websocket handle with HTTP call can be done only once
Bool_t RegisterCommand(const char *cmdname, const char *method, const char *icon=0)
Register command which can be executed from web interface.
Bool_t IsNull() const
Definition: TString.h:387
Mother of all ROOT objects.
Definition: TObject.h:37
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
Bool_t Unregister(TObject *obj)
Unregister object.
TString fContent
response header like ContentEncoding, Cache-Control and so on
Definition: THttpCallArg.h:42
virtual const char * GetTitle() const
Returns title of object.
Definition: TObject.cxx:460
void AddHeader(const char *name, const char *value)
Set name: value pair to reply header Content-Type field handled separately - one should use SetConten...
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
const Ssiz_t kNPOS
Definition: Rtypes.h:115
Bool_t Is404() const
Definition: THttpCallArg.h:310
void SetFile(const char *filename=0)
Definition: THttpCallArg.h:221
Bool_t SetIcon(const char *fullname, const char *iconname)
set name of icon, used in browser together with the item
virtual void Timeout()
Definition: TTimer.h:102
#define NULL
Definition: Rtypes.h:82
R__EXTERN Int_t gDebug
Definition: Rtypes.h:128
virtual void ClearHandle()=0
static Bool_t VerifyFilePath(const char *fname)
Checked that filename does not contains relative path below current directory Used to prevent access ...
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition: TSystem.cxx:1244
virtual const char * GetName() const
Returns name of object.
Definition: TObject.cxx:416
const Bool_t kTRUE
Definition: Rtypes.h:91
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition: TNamed.cxx:155
void SetDrawPage(const char *filename)
Set file name of HTML page, delivered by the server when objects drawing page is requested from the b...
const Int_t n
Definition: legend1.C:16
TString fFileName
item path
Definition: THttpCallArg.h:28
const char * GetMethod() const
Definition: THttpCallArg.h:149
Bool_t SetItemField(const char *fullname, const char *name, const char *value)
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
char name[80]
Definition: TGX11.cxx:109
void Restrict(const char *path, const char *options)
Restrict access to specified object.
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:911
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Definition: TString.cxx:1059
void SetDefaultPage(const char *filename)
Set file name of HTML page, delivered by the server when http address is opened in the browser...
void SetJSROOT(const char *location)
Set location of JSROOT to use with the server One could specify address like: https://root.cern.ch/js/3.3/ http://web-docs.gsi.de/~linev/js/3.3/ This allows to get new JSROOT features with old server, reduce load on THttpServer instance, also startup time can be improved When empty string specified (default), local copy of JSROOT is used (distributed with ROOT)
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition: TClass.cxx:4714
const char * Data() const
Definition: TString.h:349