24 #include "RConfigure.h" 47 class THttpTimer :
public TTimer {
76 TLongPollEngine(
const char *
name,
const char *title) :
THttpWSEngine(name, title), fPoll(0), fBuf() {}
78 virtual ~TLongPollEngine() {}
80 virtual UInt_t GetId()
const 82 const void *ptr = (
const void *)
this;
86 virtual void ClearHandle()
95 virtual void Send(
const void * ,
int )
97 Error(
"TLongPollEngine::Send",
"Should never be called, only text is supported");
100 virtual void SendCharStar(
const char *buf)
107 }
else if (fBuf.
Length() == 0) {
110 Error(
"TLongPollEngine::SendCharStar",
"Too many send operations, use TList object instead");
123 Error(
"PreviewData",
"NEVER SHOULD HAPPEN");
128 Info(
"PreviewData",
"Get dummy request when previous not completed");
209 :
TNamed("http", "
ROOT http server"), fEngines(), fTimer(0), fSniffer(0), fMainThrdId(0), fJSROOTSYS(),
210 fTopName("
ROOT"), fJSROOT(), fLocations(), fDefaultPage(), fDefaultPageCont(), fDrawPage(), fDrawPageCont(),
213 fLocations.SetOwner(
kTRUE);
215 #ifdef COMPILED_WITH_DABC 217 if (dabcsys != 0) fJSROOTSYS =
TString::Format(
"%s/plugins/root/js", dabcsys);
221 if (jsrootsys != 0) fJSROOTSYS = jsrootsys;
223 if (fJSROOTSYS.Length() == 0) {
226 Warning(
"THttpServer",
"problems resolving '%s', use JSROOTSYS to specify $ROOTSYS/etc/http location",
234 AddLocation(
"currentdir/",
".");
235 AddLocation(
"jsrootsys/", fJSROOTSYS);
238 fDefaultPage = fJSROOTSYS +
"/files/online.htm";
239 fDrawPage = fJSROOTSYS +
"/files/draw.htm";
246 if (strchr(engine,
';') == 0) {
247 CreateEngine(engine);
253 if ((strcmp(opt,
"readonly") == 0) || (strcmp(opt,
"ro") == 0)) {
254 GetSniffer()->SetReadOnly(
kTRUE);
255 }
else if ((strcmp(opt,
"readwrite") == 0) || (strcmp(opt,
"rw") == 0)) {
256 GetSniffer()->SetReadOnly(
kFALSE);
284 if (fSniffer)
delete fSniffer;
293 return fSniffer ? fSniffer->IsReadOnly() :
kTRUE;
303 if (fSniffer) fSniffer->SetReadOnly(readonly);
314 if ((prefix == 0) || (*prefix == 0))
return;
316 TNamed *obj =
dynamic_cast<TNamed *
>(fLocations.FindObject(prefix));
320 fLocations.Add(
new TNamed(prefix, path));
335 fJSROOT = location ? location :
"";
346 if ((filename != 0) && (*filename != 0))
347 fDefaultPage = filename;
349 fDefaultPage = fJSROOTSYS +
"/files/online.htm";
352 fDefaultPageCont.Clear();
363 if ((filename != 0) && (*filename != 0))
364 fDrawPage = filename;
366 fDrawPage = fJSROOTSYS +
"/files/draw.htm";
369 fDrawPageCont.Clear();
384 if (engine == 0)
return kFALSE;
386 const char *arg = strchr(engine,
':');
387 if (arg == 0)
return kFALSE;
390 if (arg != engine) clname.
Append(engine, arg - engine);
392 if ((clname.
Length() == 0) || (clname ==
"http") || (clname ==
"civetweb"))
393 clname =
"TCivetweb";
394 else if (clname ==
"fastcgi")
396 else if (clname ==
"dabc")
397 clname =
"TDabcEngine";
401 if (engine_class == 0)
return kFALSE;
404 if (eng == 0)
return kFALSE;
408 if (!eng->
Create(arg + 1)) {
435 fTimer =
new THttpTimer(milliSec, mode,
this);
446 if ((fname == 0) || (*fname == 0))
return kFALSE;
450 while (*fname != 0) {
453 const char *next = strpbrk(fname,
"/\\");
454 if (next == 0)
return kTRUE;
457 if ((next == fname + 2) && (*fname ==
'.') && (*(fname + 1) ==
'.')) {
460 if (level < 0)
return kFALSE;
465 if ((next == fname + 1) && (*fname ==
'.')) {
492 if ((uri == 0) || (strlen(uri) == 0))
return kFALSE;
496 TIter iter(&fLocations);
498 while ((obj = iter()) != 0) {
500 if (pos ==
kNPOS)
continue;
502 if (!VerifyFilePath(fname.
Data()))
return kFALSE;
504 if ((fname[0] ==
'/') && (res[res.
Length() - 1] ==
'/')) res.
Resize(res.
Length() - 1);
528 std::unique_lock<std::mutex> lk(fMutex);
548 if (can_run_immediately && (fMainThrdId != 0) && (fMainThrdId ==
TThread::SelfId())) {
554 std::unique_lock<std::mutex> lk(fMutex);
570 Error(
"ProcessRequests",
"Should be called only from main ROOT thread");
574 std::unique_lock<std::mutex> lk(fMutex, std::defer_lock);
579 if (fCallArgs.GetSize() > 0) {
581 fCallArgs.RemoveFirst();
587 fSniffer->SetCurrentCallArg(arg);
591 fSniffer->SetCurrentCallArg(0);
593 fSniffer->SetCurrentCallArg(0);
601 TIter iter(&fEngines);
616 if (fDefaultPageCont.Length() == 0) {
618 char *buf = ReadFileContent(fDefaultPage.Data(), len);
619 if (len > 0) fDefaultPageCont.Append(buf, len);
623 if (fDefaultPageCont.Length() == 0) {
629 if (fJSROOT.Length() > 0) {
631 if (!repl.
EndsWith(
"/")) repl +=
"/";
635 const char *hjsontag =
"\"$$$h.json$$$\"";
641 const char *topname = fTopName.Data();
643 fSniffer->ScanHierarchy(topname, arg->
fPathName.
Data(), &store);
648 "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
657 if (fDrawPageCont.Length() == 0) {
659 char *buf = ReadFileContent(fDrawPage.Data(), len);
660 if (len > 0) fDrawPageCont.Append(buf, len);
664 if (fDrawPageCont.Length() == 0) {
667 const char *rootjsontag =
"\"$$$root.json$$$\"";
668 const char *hjsontag =
"\"$$$h.json$$$\"";
673 if (fJSROOT.Length() > 0) {
675 if (!repl.
EndsWith(
"/")) repl +=
"/";
682 const char *topname = fTopName.Data();
693 if (fSniffer->Produce(arg->
fPathName.
Data(),
"root.json",
"compact=3", bindata, bindatalen, str)) {
698 "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
706 arg->
SetFile(fJSROOTSYS +
"/img/RootIcon.ico");
726 if ((filename ==
"h.xml") || (filename ==
"get.xml")) {
730 arg->
fContent.
Form(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
737 const char *topname = fTopName.Data();
739 fSniffer->ScanHierarchy(topname, arg->
fPathName.
Data(), &store, filename ==
"get.xml");
746 }
else if (filename ==
"h.json") {
748 const char *topname = fTopName.Data();
750 fSniffer->ScanHierarchy(topname, arg->
fPathName.
Data(), &store);
752 }
else if (filename ==
"root.websocket") {
760 }
else if (strcmp(arg->
GetMethod(),
"WS_CONNECT") == 0) {
765 }
else if (strcmp(arg->
GetMethod(),
"WS_READY") == 0) {
768 if (
gDebug > 0)
Info(
"ProcessRequest",
"Set WebSocket handle %p", wshandle);
773 }
else if (strcmp(arg->
GetMethod(),
"WS_DATA") == 0) {
779 }
else if (strcmp(arg->
GetMethod(),
"WS_CLOSE") == 0) {
785 if (
gDebug > 0)
Info(
"ProcessRequest",
"Clear WebSocket handle");
795 }
else if (filename ==
"root.longpoll") {
802 }
else if (arg->
fQuery ==
"connect") {
810 TLongPollEngine *handle =
new TLongPollEngine(
"longpoll", arg->
fPathName.
Data());
837 Int_t len = strlen(post);
838 void *buf =
malloc(len / 2 + 1);
839 char *sbuf = (
char *)buf;
845 if (
false ) arg->
Set404();
851 if (bindata != 0) arg->
SetBinData(bindata, bindatalen);
860 if (arg->
Is404())
return;
864 if (filename ==
"root.bin") {
867 const char *parname = fSniffer->IsStreamerInfoItem(arg->
fPathName.
Data()) ?
"BVersion" :
"MVersion";
868 arg->
AddHeader(parname,
Form(
"%u", (
unsigned)fSniffer->GetStreamerInfoHash()));
873 "private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0");
883 return fSniffer->RegisterObject(subfolder, obj);
893 return fSniffer->UnregisterObject(obj);
903 fSniffer->Restrict(path, options);
936 return fSniffer->RegisterCommand(cmdname, method, icon);
944 return SetItemField(foldername,
"_hidden", hide ?
"true" : (
const char *)0);
955 return SetItemField(fullname,
"_icon", iconname);
962 return fSniffer->CreateItem(fullname, title);
969 return fSniffer->SetItemField(fullname, name, value);
976 return fSniffer->GetItemField(fullname, name);
984 static const struct {
989 {
".json", 5,
"application/json"},
990 {
".bin", 4,
"application/x-binary"},
991 {
".gif", 4,
"image/gif"},
992 {
".jpg", 4,
"image/jpeg"},
993 {
".png", 4,
"image/png"},
994 {
".html", 5,
"text/html"},
995 {
".htm", 4,
"text/html"},
996 {
".shtm", 5,
"text/html"},
997 {
".shtml", 6,
"text/html"},
998 {
".css", 4,
"text/css"},
999 {
".js", 3,
"application/x-javascript"},
1000 {
".ico", 4,
"image/x-icon"},
1001 {
".jpeg", 5,
"image/jpeg"},
1002 {
".svg", 4,
"image/svg+xml"},
1003 {
".txt", 4,
"text/plain"},
1004 {
".torrent", 8,
"application/x-bittorrent"},
1005 {
".wav", 4,
"audio/x-wav"},
1006 {
".mp3", 4,
"audio/x-mp3"},
1007 {
".mid", 4,
"audio/mid"},
1008 {
".m3u", 4,
"audio/x-mpegurl"},
1009 {
".ogg", 4,
"application/ogg"},
1010 {
".ram", 4,
"audio/x-pn-realaudio"},
1011 {
".xslt", 5,
"application/xml"},
1012 {
".xsl", 4,
"application/xml"},
1013 {
".ra", 3,
"audio/x-pn-realaudio"},
1014 {
".doc", 4,
"application/msword"},
1015 {
".exe", 4,
"application/octet-stream"},
1016 {
".zip", 4,
"application/x-zip-compressed"},
1017 {
".xls", 4,
"application/excel"},
1018 {
".tgz", 4,
"application/x-tar-gz"},
1019 {
".tar", 4,
"application/x-tar"},
1020 {
".gz", 3,
"application/x-gunzip"},
1021 {
".arj", 4,
"application/x-arj-compressed"},
1022 {
".rar", 4,
"application/x-arj-compressed"},
1023 {
".rtf", 4,
"application/rtf"},
1024 {
".pdf", 4,
"application/pdf"},
1025 {
".swf", 4,
"application/x-shockwave-flash"},
1026 {
".mpg", 4,
"video/mpeg"},
1027 {
".webm", 5,
"video/webm"},
1028 {
".mpeg", 5,
"video/mpeg"},
1029 {
".mov", 4,
"video/quicktime"},
1030 {
".mp4", 4,
"video/mp4"},
1031 {
".m4v", 4,
"video/x-m4v"},
1032 {
".asf", 4,
"video/x-ms-asf"},
1033 {
".avi", 4,
"video/x-msvideo"},
1034 {
".bmp", 4,
"image/bmp"},
1035 {
".ttf", 4,
"application/x-font-ttf"},
1038 int path_len = strlen(path);
1048 return "text/plain";
1058 std::ifstream is(filename);
1061 is.seekg(0, is.end);
1063 is.seekg(0, is.beg);
1065 char *buf = (
char *)
malloc(len);
void SetZipping(Int_t kind)
Set kind of content zipping 0 - none 1 - only when supported in request header 2 - if supported and c...
virtual void Process()
Method regularly called in main ROOT context.
Namespace for new ROOT classes and functions.
UInt_t GetWSId() const
get web-socket id
void SetWSHandle(TNamed *handle)
assign websocket handle with HTTP call
Storage of hierarchy scan in TRootSniffer in JSON format.
This class represents a WWW compatible URL.
TString & ReplaceAll(const TString &s1, const TString &s2)
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
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
TString fQuery
! additional arguments
static const TString & GetRootSys()
Get the rootsys directory in the installation. Static utility function.
TObject * At(Int_t idx) const
void SetServer(THttpServer *serv)
virtual void AssignCanvas(TCanvas *canv)
assign canvas to the web socket connects with CanvasModified signal
const char * GetQuery() const
returns request query (string after ? in request URL)
void SetContentType(const char *typ)
set content type like "text/xml" or "application/json"
void SetPostponed()
mark reply as postponed - submitting thread will not be inform
void ProcessRequests()
Process submitted requests, must be called from main thread.
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Bool_t CreateItem(const char *fullname, const char *title)
void SetContent(const char *c)
Set content directly.
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
void SetPostData(void *data, Long_t length)
set data, posted with the request buffer should be allocated with malloc(length+1) call...
The TNamed class is the base class for all named ROOT classes.
TCanvasImp * GetCanvasImp() const
Get canvas implementation pointer if any.
virtual const char * Getenv(const char *env)
Get environment variable.
TString fPathName
! item path
void Set404()
mark reply as 404 error - page/request not exists or refused
void Info(const char *location, const char *msgfmt,...)
TString & Append(const char *cs)
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
std::vector< std::vector< double > > Data
static Long_t SelfId()
Static method returning the id for the current thread.
void SetTimer(Long_t milliSec=100, Bool_t mode=kTRUE)
create timer which will invoke ProcessRequests() function periodically Timer is required to perform a...
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
void Error(const char *location, const char *msgfmt,...)
Storage of hierarchy scan in TRootSniffer in XML format.
Bool_t SubmitHttp(THttpCallArg *arg, Bool_t can_run_immediately=kFALSE)
Submit HTTP request.
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
! top item name
Int_t GetLast() const
Return index of last object in array.
void SetBinData(void *data, Long_t length)
set binary data, which will be returned as reply body
void SetJson()
set content type as JSON
Bool_t CreateEngine(const char *engine)
factory method to create different http engines At the moment two engine kinds are supported: civetwe...
Bool_t fNotifyFlag
! indicate that notification called
R__EXTERN TSystem * gSystem
virtual TObject * GetPrimitive(const char *name) const
Get primitive.
static const struct @164 builtin_mime_types[]
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
virtual ~THttpServer()
destructor delete all http engines and sniffer
char * Form(const char *fmt,...)
virtual Bool_t Create(const char *)
Method to create all components of engine.
Handles synchronous and a-synchronous timer events.
The ROOT global object gROOT contains a list of all defined classes.
void Warning(const char *location, const char *msgfmt,...)
void SetSniffer(TRootSniffer *sniff)
Set TRootSniffer to the server Server takes ownership over sniffer.
Bool_t ExecuteHttp(THttpCallArg *arg)
Execute HTTP request.
TString & Remove(Ssiz_t pos)
std::condition_variable fCond
! condition used to wait for processing
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.
Bool_t Hide(const char *fullname, Bool_t hide=kTRUE)
hides folder or element from web gui
void SetWSId(UInt_t id)
set web-socket id
virtual void ProcessRequest(THttpCallArg *arg)
Function called for every processed request.
void SetMethod(const char *method)
set request method kind like GET or POST
void SetXml()
set content type as XML
static const TString & GetEtcDir()
Get the sysconfig directory in the installation. Static utility function.
static TString BaseConvert(const TString &s_in, Int_t base_in, Int_t base_out)
Converts string from base base_in to base base_out.
void NotifyCondition()
method used to notify condition which waiting when operation will complete Condition notified only if...
TNamed * TakeWSHandle()
takeout websocket handle with HTTP call can be done only once
void ParseOptions() const
Parse URL options into a key/value map.
Bool_t RegisterCommand(const char *cmdname, const char *method, const char *icon=0)
Register command which can be executed from web interface.
Mother of all ROOT objects.
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
Bool_t Unregister(TObject *obj)
Unregister object.
TString fContent
! text content (if any)
virtual const char * GetTitle() const
Returns title of object.
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.
void SetFile(const char *filename=0)
indicate that http request should response with file content
Bool_t SetIcon(const char *fullname, const char *iconname)
set name of icon, used in browser together with the item
void SetOptions(const char *opt)
Bool_t IsContentType(const char *typ) const
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 ~.
Int_t GetIntValueFromOptions(const char *key) const
Return a value for a given key from the URL options as an Int_t, a missing key returns -1...
virtual const char * GetName() const
Returns name of object.
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
void SetDrawPage(const char *filename)
Set file name of HTML page, delivered by the server when objects drawing page is requested from the b...
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
TString fFileName
! file name
const char * GetMethod() const
returns request method like GET or POST
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.
void Restrict(const char *path, const char *options)
Restrict access to specified object.
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
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.
const char * Data() const