22#include "RConfigure.h"
144 jsrootsys =
gEnv->
GetValue(
"HttpServ.JSRootPath", jsrootsys);
146 if (jsrootsys && *jsrootsys) {
147 if ((strncmp(jsrootsys,
"http://", 7)==0) || (strncmp(jsrootsys,
"https://", 8)==0))
156 ::Warning(
"THttpServer::THttpServer",
"problems resolving '%s', set JSROOTSYS to proper JavaScript ROOT location",
172 if (strstr(engine,
"basic_sniffer")) {
177 sniff = (
TRootSniffer *)
gROOT->ProcessLineSync(
"new TRootSnifferFull(\"sniff\");");
185 if (strchr(engine,
';') == 0) {
192 if ((strcmp(opt,
"readonly") == 0) || (strcmp(opt,
"ro") == 0)) {
194 }
else if ((strcmp(opt,
"readwrite") == 0) || (strcmp(opt,
"rw") == 0)) {
196 }
else if (strcmp(opt,
"global") == 0) {
198 }
else if (strcmp(opt,
"noglobal") == 0) {
200 }
else if (strncmp(opt,
"cors=", 5) == 0) {
202 }
else if (strcmp(opt,
"cors") == 0) {
222 while (
auto engine =
dynamic_cast<THttpEngine *
>(iter()))
296 if (!prefix || (*prefix == 0))
316 fJSROOT = location ? location :
"";
327 if (!filename.empty())
344 if (!filename.empty())
368 const char *arg = strchr(engine,
':');
374 clname.
Append(engine, arg - engine);
378 if ((clname.
Length() == 0) || (clname ==
"http") || (clname ==
"civetweb")) {
380 }
else if (clname ==
"https") {
382 }
else if (clname ==
"fastcgi") {
399 if (!eng->
Create(arg + 1)) {
433 Error(
"SetTimer",
"Server runs already in special thread, therefore no any timer can be created");
435 fTimer = std::make_unique<THttpTimer>(milliSec, mode, *
this);
459 std::thread thrd([
this] {
469 std::this_thread::sleep_for(std::chrono::milliseconds(1));
474 fThrd = std::move(thrd);
497 if (!fname || (*fname == 0))
502 while (*fname != 0) {
505 const char *next = strpbrk(fname,
"/\\");
510 if ((next == fname + 2) && (*fname ==
'.') && (*(fname + 1) ==
'.')) {
519 if ((next == fname + 1) && (*fname ==
'.')) {
546 if (!uri || (*uri == 0))
555 fname.
Remove(0, pos + (entry.first.length() - 1));
558 res = entry.second.c_str();
559 if ((fname[0] ==
'/') && (res[res.
Length() - 1] ==
'/'))
587 std::unique_lock<std::mutex> lk(
fMutex);
613 arg->NotifyCondition();
618 std::unique_lock<std::mutex> lk(
fMutex);
638 Error(
"ProcessRequests",
"Should be called only from main ROOT thread");
644 std::unique_lock<std::mutex> lk(
fMutex, std::defer_lock);
648 std::shared_ptr<THttpCallArg> arg;
651 if (!
fArgs.empty()) {
660 if (arg->fFileName ==
"root_batch_holder.js") {
665 fSniffer->SetCurrentCallArg(arg.get());
670 fSniffer->SetCurrentCallArg(
nullptr);
672 fSniffer->SetCurrentCallArg(
nullptr);
675 arg->NotifyCondition();
681 while ((engine = (
THttpEngine *)iter()) !=
nullptr) {
707 auto wsptr =
FindWS(arg->GetPathName());
709 if (!wsptr || !wsptr->ProcessBatchHolder(arg)) {
711 arg->NotifyCondition();
721 std::string arr =
"[";
724 std::lock_guard<std::mutex> grd(
fWSMutex);
726 if (arr.length() > 1)
729 arr.append(
Form(
"{ name: \"%s\", title: \"%s\" }",
ws->GetName(),
ws->GetTitle()));
737 std::string arg =
"\"$$$wslist$$$\"";
739 auto pos = res.find(arg);
740 if (pos != std::string::npos)
741 res.replace(pos, arg.length(), arr);
758 if (repl.back() !=
'/')
762 if (arg->fPathName.Length() > 0) cnt++;
763 for (
Int_t n = 1;
n < arg->fPathName.Length()-1; ++
n)
764 if (arg->fPathName[
n] ==
'/') {
765 if (arg->fPathName[
n-1] !=
'/') {
775 while (cnt-- >0) repl.append(
"../");
776 repl.append(
"jsrootsys/");
781 arg->ReplaceAllinContent(
"=\"jsrootsys/", repl);
797 if ((arg->fFileName ==
"root.websocket") || (arg->fFileName ==
"root.longpoll")) {
802 if (arg->fFileName.IsNull() || (arg->fFileName ==
"index.htm") || (arg->fFileName ==
"default.htm")) {
804 if (arg->fFileName ==
"default.htm") {
810 auto wsptr =
FindWS(arg->GetPathName());
812 auto handler = wsptr.get();
821 if (arg->fContent.find(
"file:") == 0) {
822 const char *fname = arg->fContent.c_str() + 5;
828 handler->VerifyDefaultPageContent(arg);
832 if (arg->fContent.empty() && arg->fFileName.IsNull() && arg->fPathName.IsNull() &&
IsWSOnly()) {
836 if (arg->fContent.empty() && !
IsWSOnly()) {
844 if (arg->fContent.empty()) {
847 }
else if (!arg->Is404()) {
851 const char *hjsontag =
"\"$$$h.json$$$\"";
854 if (arg->fContent.find(hjsontag) != std::string::npos) {
858 if (arg->fTopName.Length() > 0)
859 topname = arg->fTopName.Data();
860 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
862 arg->ReplaceAllinContent(hjsontag, h_json.
Data());
864 arg->AddNoCacheHeader();
866 if (arg->fQuery.Index(
"nozip") ==
kNPOS)
869 arg->SetContentType(
"text/html");
874 if ((arg->fFileName ==
"draw.htm") && !
IsWSOnly()) {
881 const char *rootjsontag =
"\"$$$root.json$$$\"";
882 const char *hjsontag =
"\"$$$h.json$$$\"";
888 if ((arg->fQuery.Index(
"no_h_json") ==
kNPOS) && (arg->fQuery.Index(
"webcanvas") ==
kNPOS) &&
889 (arg->fContent.find(hjsontag) != std::string::npos)) {
893 if (arg->fTopName.Length() > 0)
894 topname = arg->fTopName.Data();
895 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store,
kTRUE);
897 arg->ReplaceAllinContent(hjsontag, h_json.
Data());
900 if ((arg->fQuery.Index(
"no_root_json") ==
kNPOS) && (arg->fQuery.Index(
"webcanvas") ==
kNPOS) &&
901 (arg->fContent.find(rootjsontag) != std::string::npos)) {
903 if (
fSniffer->Produce(arg->fPathName.Data(),
"root.json",
"compact=23", str))
904 arg->ReplaceAllinContent(rootjsontag, str);
906 arg->AddNoCacheHeader();
907 if (arg->fQuery.Index(
"nozip") ==
kNPOS)
909 arg->SetContentType(
"text/html");
914 if ((arg->fFileName ==
"favicon.ico") && arg->fPathName.IsNull()) {
915 arg->SetFile(
fJSROOTSYS +
"/img/RootIcon.ico");
921 arg->SetFile(filename);
926 if (!arg->fPathName.IsNull() && !arg->fFileName.IsNull()) {
927 TString wsname = arg->fPathName, fname;
928 auto pos = wsname.
First(
'/');
930 wsname = arg->fPathName;
932 wsname = arg->fPathName(0, pos);
933 fname = arg->fPathName(pos + 1, arg->fPathName.Length() - pos);
937 fname.
Append(arg->fFileName);
943 if (
ws &&
ws->CanServeFiles()) {
944 TString fdir =
ws->GetDefaultPageContent();
946 if (fdir.
Index(
"file:") == 0) {
948 auto separ = fdir.
Last(
'/');
962 filename = arg->fFileName;
971 if (arg->fContent.empty())
973 }
else if ((filename ==
"h.xml") || (filename ==
"get.xml")) {
975 Bool_t compact = arg->fQuery.Index(
"compact") !=
kNPOS;
979 res.
Form(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
989 if (arg->fTopName.Length() > 0)
990 topname = arg->fTopName.Data();
991 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store, filename ==
"get.xml");
998 arg->SetContent(std::string(res.
Data()));
1001 }
else if (filename ==
"h.json") {
1005 if (arg->fTopName.Length() > 0)
1006 topname = arg->fTopName.Data();
1007 fSniffer->ScanHierarchy(topname, arg->fPathName.Data(), &store);
1008 arg->SetContent(std::string(res.
Data()));
1010 }
else if (
fSniffer->Produce(arg->fPathName.Data(), filename.
Data(), arg->fQuery.Data(), arg->fContent)) {
1024 if (filename ==
"root.bin") {
1027 const char *parname =
fSniffer->IsStreamerInfoItem(arg->fPathName.Data()) ?
"BVersion" :
"MVersion";
1028 arg->AddHeader(parname,
Form(
"%u", (
unsigned)
fSniffer->GetStreamerInfoHash()));
1032 arg->AddNoCacheHeader();
1036 arg->AddHeader(
"Access-Control-Allow-Origin",
GetCors());
1046 return fSniffer->RegisterObject(subfolder, obj);
1056 return fSniffer->UnregisterObject(obj);
1066 std::lock_guard<std::mutex> grd(
fWSMutex);
1075 std::lock_guard<std::mutex> grd(
fWSMutex);
1088 std::lock_guard<std::mutex> grd(
fWSMutex);
1090 if (strcmp(
name,
ws->GetName()) == 0)
1107 auto wsptr =
FindWS(arg->GetPathName());
1109 auto handler = wsptr.get();
1111 if (!handler && !external_thrd)
1114 if (external_thrd && (!handler || !handler->AllowMTProcess())) {
1115 std::unique_lock<std::mutex> lk(
fMutex);
1119 arg->fCond.wait(lk);
1129 if (arg->fFileName ==
"root.websocket") {
1131 process = handler->HandleWS(arg);
1132 }
else if (arg->fFileName ==
"root.longpoll") {
1134 if (arg->fQuery.BeginsWith(
"raw_connect") || arg->fQuery.BeginsWith(
"txt_connect")) {
1137 arg->SetMethod(
"WS_CONNECT");
1139 bool israw = arg->fQuery.BeginsWith(
"raw_connect");
1144 if (handler->HandleWS(arg)) {
1145 arg->SetMethod(
"WS_READY");
1147 if (handler->HandleWS(arg))
1148 arg->SetTextContent(std::string(israw ?
"txt:" :
"") + std::to_string(arg->GetWSId()));
1150 arg->TakeWSEngine();
1153 process = arg->IsText();
1160 arg->SetWSId(std::stoul(connid));
1162 arg->SetMethod(
"WS_CLOSE");
1163 arg->SetTextContent(
"OK");
1165 arg->SetMethod(
"WS_DATA");
1168 process = handler->HandleWS(arg);
1218 return fSniffer->RegisterCommand(cmdname, method, icon);
1226 return SetItemField(foldername,
"_hidden", hide ?
"true" : (
const char *)0);
1244 return fSniffer->CreateItem(fullname, title);
1266 static const struct {
1271 {
".json", 5,
"application/json"},
1272 {
".bin", 4,
"application/x-binary"},
1273 {
".gif", 4,
"image/gif"},
1274 {
".jpg", 4,
"image/jpeg"},
1275 {
".png", 4,
"image/png"},
1276 {
".html", 5,
"text/html"},
1277 {
".htm", 4,
"text/html"},
1278 {
".shtm", 5,
"text/html"},
1279 {
".shtml", 6,
"text/html"},
1280 {
".css", 4,
"text/css"},
1281 {
".js", 3,
"application/x-javascript"},
1282 {
".mjs", 4,
"text/javascript"},
1283 {
".ico", 4,
"image/x-icon"},
1284 {
".jpeg", 5,
"image/jpeg"},
1285 {
".svg", 4,
"image/svg+xml"},
1286 {
".txt", 4,
"text/plain"},
1287 {
".torrent", 8,
"application/x-bittorrent"},
1288 {
".wav", 4,
"audio/x-wav"},
1289 {
".mp3", 4,
"audio/x-mp3"},
1290 {
".mid", 4,
"audio/mid"},
1291 {
".m3u", 4,
"audio/x-mpegurl"},
1292 {
".ogg", 4,
"application/ogg"},
1293 {
".ram", 4,
"audio/x-pn-realaudio"},
1294 {
".xslt", 5,
"application/xml"},
1295 {
".xsl", 4,
"application/xml"},
1296 {
".ra", 3,
"audio/x-pn-realaudio"},
1297 {
".doc", 4,
"application/msword"},
1298 {
".exe", 4,
"application/octet-stream"},
1299 {
".zip", 4,
"application/x-zip-compressed"},
1300 {
".xls", 4,
"application/excel"},
1301 {
".tgz", 4,
"application/x-tar-gz"},
1302 {
".tar", 4,
"application/x-tar"},
1303 {
".gz", 3,
"application/x-gunzip"},
1304 {
".arj", 4,
"application/x-arj-compressed"},
1305 {
".rar", 4,
"application/x-arj-compressed"},
1306 {
".rtf", 4,
"application/rtf"},
1307 {
".pdf", 4,
"application/pdf"},
1308 {
".swf", 4,
"application/x-shockwave-flash"},
1309 {
".mpg", 4,
"video/mpeg"},
1310 {
".webm", 5,
"video/webm"},
1311 {
".mpeg", 5,
"video/mpeg"},
1312 {
".mov", 4,
"video/quicktime"},
1313 {
".mp4", 4,
"video/mp4"},
1314 {
".m4v", 4,
"video/x-m4v"},
1315 {
".asf", 4,
"video/x-ms-asf"},
1316 {
".avi", 4,
"video/x-msvideo"},
1317 {
".bmp", 4,
"image/bmp"},
1318 {
".ttf", 4,
"application/x-font-ttf"},
1319 {
".woff", 5,
"font/woff"},
1320 {
".woff2", 6,
"font/woff2"},
1323 int path_len = strlen(path);
1334 return "text/plain";
1344 std::ifstream is(filename, std::ios::in | std::ios::binary);
1348 is.seekg(0, is.end);
1350 is.seekg(0, is.beg);
1352 char *buf = (
char *)
malloc(len);
1368 std::ifstream is(filename, std::ios::in | std::ios::binary);
1371 is.seekg(0, std::ios::end);
1372 res.resize(is.tellg());
1373 is.seekg(0, std::ios::beg);
1374 is.read((
char *)res.data(), res.length());
char * Form(const char *fmt,...)
R__EXTERN TSystem * gSystem
static const struct @144 builtin_mime_types[]
TClass instances represent classes, structs and namespaces in the ROOT type system.
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
static TClass * LoadClass(const char *requestedname, Bool_t silent)
Helper function used by TClass::GetClass().
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
void Set404()
mark reply as 404 error - page/request not exists or refused
void SetServer(THttpServer *serv)
virtual Bool_t Create(const char *)
Method to create all components of engine.
virtual void Process()
Method regularly called in main ROOT context.
virtual void Terminate()
Method called when server want to be terminated.
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
virtual void ProcessRequest(std::shared_ptr< THttpCallArg > arg)
Process single http request Depending from requested path and filename different actions will be perf...
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
void SetTimer(Long_t milliSec=100, Bool_t mode=kTRUE)
create timer which will invoke ProcessRequests() function periodically Timer is required to perform a...
virtual void ProcessBatchHolder(std::shared_ptr< THttpCallArg > &arg)
Process special http request for root_batch_holder.js script This kind of requests used to hold web b...
std::vector< std::shared_ptr< THttpWSHandler > > fWSHandlers
! list of WS handlers
virtual ~THttpServer()
destructor delete all http engines and sniffer
void SetTerminate()
set termination flag, no any further requests will be processed
virtual void MissedRequest(THttpCallArg *arg)
Method called when THttpServer cannot process request By default such requests replied with 404 code ...
Bool_t fOwnThread
! true when specialized thread allocated for processing requests
void SetSniffer(TRootSniffer *sniff)
Set TRootSniffer to the server Server takes ownership over sniffer.
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) In read-only server is not allowed to change any ROOT ...
const char * GetItemField(const char *fullname, const char *name)
const char * GetCors() const
Returns specified CORS domain.
std::thread fThrd
! own thread
void StopServerThread()
Stop server thread Normally called shortly before http server destructor.
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"
void SetDrawPage(const std::string &filename="")
Set file name of HTML page, delivered by the server when objects drawing page is requested from the b...
Bool_t CreateItem(const char *fullname, const char *title)
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 one could map some system folder to the server ...
std::map< std::string, std::string > fLocations
! list of local directories, which could be accessed via server
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
TString fJSROOTSYS
! location of local JSROOT files
std::unique_ptr< THttpTimer > fTimer
! timer used to access main thread
Bool_t fWSOnly
! when true, handle only websockets / longpoll engine
Bool_t Register(const char *subfolder, TObject *obj)
Register object in subfolder.
TList fEngines
! engines which runs http server
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
void SetDefaultPage(const std::string &filename="")
Set file name of HTML page, delivered by the server when http address is opened in the browser.
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
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/..." Either using pre-configured JSROOT installation from web...
std::string BuildWSEntryPage()
Create summary page with active WS handlers.
Bool_t IsWSOnly() const
returns true if only websockets are handled by the server Typically used by WebGui
std::mutex fWSMutex
! mutex to protect WS handler lists
Bool_t CreateEngine(const char *engine)
factory method to create different http engines At the moment two engine kinds are supported: civetwe...
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
std::string fDefaultPageCont
! content of default html page
static Bool_t VerifyFilePath(const char *fname)
Checked that filename does not contains relative path below current directory Used to prevent access ...
Bool_t SetItemField(const char *fullname, const char *name, const char *value)
void SetJSROOT(const char *location)
Set location of JSROOT to use with the server One could specify address like: https://root....
std::mutex fMutex
! mutex to protect list with arguments
std::string fDefaultPage
! file name for default page name
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
Bool_t fTerminated
! termination flag, disables all requests processing
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
THttpTimer(Long_t milliSec, Bool_t mode, THttpServer &serv)
!< server processing requests
virtual TString GetDefaultPageContent()
Provides content of default web page for registered web-socket handler Can be content of HTML page or...
virtual void Add(TObject *obj)
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
The TNamed class is the base class for all named ROOT classes.
Int_t GetLast() const
Return index of last object in array.
TObject * At(Int_t idx) const
Mother of all ROOT objects.
virtual const char * GetName() const
Returns name of object.
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
static const TString & GetRootSys()
Get the rootsys directory in the installation. Static utility function.
static const TString & GetDataDir()
Get the data directory in the installation. Static utility function.
Storage of hierarchy scan in TRootSniffer in JSON format.
Storage of hierarchy scan in TRootSniffer in XML format.
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.
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Ssiz_t First(char c) const
Find first occurrence of a character c.
const char * Data() const
void Resize(Ssiz_t n)
Resize the string. Truncate or add blanks as necessary.
Ssiz_t Last(char c) const
Find last occurrence of a character c.
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
TString & Remove(Ssiz_t pos)
TString & Append(const char *cs)
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
virtual const char * Getenv(const char *env)
Get environment variable.
static Long_t SelfId()
Static method returning the id for the current thread.
Handles synchronous and a-synchronous timer events.
This class represents a WWW compatible URL.
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
void SetOptions(const char *opt)
void ParseOptions() const
Parse URL options into a key/value map.
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.