Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TCivetweb.cxx
Go to the documentation of this file.
1// Author: Sergey Linev 21/12/2013
2
3/*************************************************************************
4 * Copyright (C) 1995-2022, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "TCivetweb.h"
12
13#include <cstdlib>
14#include <cstring>
15
16#ifdef _MSC_VER
17#include <windows.h>
18#include <tchar.h>
19#endif
20
21#include "THttpServer.h"
22#include "THttpWSEngine.h"
23#include "TUrl.h"
24#include "TSystem.h"
25#include "TError.h"
26
27//////////////////////////////////////////////////////////////////////////
28/// TCivetwebWSEngine
29///
30/// Implementation of THttpWSEngine for Civetweb
31
33protected:
35
36 /// True websocket requires extra thread to parallelize sending
37 Bool_t SupportSendThrd() const override { return kTRUE; }
38
39public:
41
42 ~TCivetwebWSEngine() override = default;
43
44 UInt_t GetId() const override { return TString::Hash((void *)&fWSconn, sizeof(void *)); }
45
46 void ClearHandle(Bool_t terminate) override
47 {
48 if (fWSconn && terminate)
50 fWSconn = nullptr;
51 }
52
53 void Send(const void *buf, int len) override
54 {
55 if (fWSconn)
57 }
58
59 /////////////////////////////////////////////////////////
60 /// Special method to send binary data with text header
61 /// For normal websocket it is two separated operation, for other engines could be combined together,
62 /// but emulates as two messages on client side
63 void SendHeader(const char *hdr, const void *buf, int len) override
64 {
65 if (fWSconn) {
68 }
69 }
70
71 void SendCharStar(const char *str) override
72 {
73 if (fWSconn)
75 }
76};
77
78//////////////////////////////////////////////////////////////////////////
79/// Check if engine has enough threads to process connect to new websocket handle
80
82{
84 if (longpoll) num_avail++;
85
86 if ((num_avail <= 0.1 * engine->GetNumThreads()) || (num_avail <= 2)) {
87 const char *cfg = engine->IsWebGui() ? "WebGui.HttpThreads parameter in rootrc" : "thrds=N parameter in config URL";
88 const char *place = longpoll ? "TCivetweb::LongpollHandler" : "TCivetweb::WebSocketHandler";
89 ::Error(place, "Only %d threads are available, reject connection request for %s. Increase %s, now it is %d", num_avail, uri, cfg, engine->GetNumThreads());
90 return kFALSE;
91 }
92
93 return kTRUE;
94}
95
96//////////////////////////////////////////////////////////////////////////
97
98int websocket_connect_handler(const struct mg_connection *conn, void *)
99{
100 const struct mg_request_info *request_info = mg_get_request_info(conn);
101 if (!request_info)
102 return 1;
103
104 TCivetweb *engine = (TCivetweb *)request_info->user_data;
105 if (!engine || engine->IsTerminating())
106 return 1;
107 THttpServer *serv = engine->GetServer();
108 if (!serv)
109 return 1;
110
111 auto arg = std::make_shared<THttpCallArg>();
112 arg->SetPathAndFileName(request_info->local_uri); // path and file name
113 arg->SetQuery(request_info->query_string); // query arguments
114 arg->SetTopName(engine->GetTopName());
115 arg->SetWSId(TString::Hash((void *)&conn, sizeof(void *)));
116 arg->SetMethod("WS_CONNECT");
117
118 if (!CheckEngineThreads(engine, arg->GetPathName(), kFALSE))
119 return 1;
120
121 Bool_t execres = serv->ExecuteWS(arg, kTRUE, kTRUE);
122
123 return execres && !arg->Is404() ? 0 : 1;
124}
125
126//////////////////////////////////////////////////////////////////////////
127
128void websocket_ready_handler(struct mg_connection *conn, void *)
129{
130 const struct mg_request_info *request_info = mg_get_request_info(conn);
131
132 TCivetweb *engine = (TCivetweb *)request_info->user_data;
133 if (!engine || engine->IsTerminating())
134 return;
135 THttpServer *serv = engine->GetServer();
136 if (!serv)
137 return;
138
139 engine->ChangeNumActiveThrerads(1);
140
141 auto arg = std::make_shared<THttpCallArg>();
142 arg->SetPathAndFileName(request_info->local_uri); // path and file name
143 arg->SetQuery(request_info->query_string); // query arguments
144 arg->SetTopName(engine->GetTopName());
145 arg->SetMethod("WS_READY");
146
147 // delegate ownership to the arg, id will be automatically set
148 arg->CreateWSEngine<TCivetwebWSEngine>(conn);
149
150 serv->ExecuteWS(arg, kTRUE, kTRUE);
151}
152
153
154//////////////////////////////////////////////////////////////////////////
155
156void websocket_close_handler(const struct mg_connection *conn, void *)
157{
158 const struct mg_request_info *request_info = mg_get_request_info(conn);
159
160 // check if connection was already closed
161 if (mg_get_user_connection_data(conn) == (void *) conn)
162 return;
163
164 TCivetweb *engine = (TCivetweb *)request_info->user_data;
165 if (!engine || engine->IsTerminating())
166 return;
167 THttpServer *serv = engine->GetServer();
168 if (!serv)
169 return;
170
171 auto arg = std::make_shared<THttpCallArg>();
172 arg->SetPathAndFileName(request_info->local_uri); // path and file name
173 arg->SetQuery(request_info->query_string); // query arguments
174 arg->SetTopName(engine->GetTopName());
175 arg->SetWSId(TString::Hash((void *)&conn, sizeof(void *)));
176 arg->SetMethod("WS_CLOSE");
177
178 serv->ExecuteWS(arg, kTRUE, kFALSE); // do not wait for result of execution
179
180 engine->ChangeNumActiveThrerads(-1);
181}
182
183//////////////////////////////////////////////////////////////////////////
184
185int websocket_data_handler(struct mg_connection *conn, int code, char *data, size_t len, void *)
186{
187 const struct mg_request_info *request_info = mg_get_request_info(conn);
188
189 // check if connection data set to connection itself - means connection was closed already
190 std::string *conn_data = (std::string *) mg_get_user_connection_data(conn);
191 if ((void *) conn_data == (void *) conn)
192 return 1;
193
194 // see https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
195 int fin = code & 0x80, opcode = code & 0x0F;
196
197 // recognized operation codes, all other should fails
198 enum { OP_CONTINUE = 0, OP_TEXT = 1, OP_BINARY = 2, OP_CLOSE = 8 };
199
200 // close when normal close is detected
201 if (fin && (opcode == OP_CLOSE)) {
202 if (conn_data) delete conn_data;
203 websocket_close_handler(conn, nullptr);
204 mg_set_user_connection_data(conn, conn); // mark connection as closed
205 return 1;
206 }
207
208 // ignore empty data
209 if (len == 0)
210 return 1;
211
212 // close connection when unrecognized opcode is detected
213 if ((opcode != OP_CONTINUE) && (opcode != OP_TEXT) && (opcode != OP_BINARY)) {
214 if (conn_data) delete conn_data;
215 websocket_close_handler(conn, nullptr);
216 mg_set_user_connection_data(conn, conn); // mark connection as closed
217 return 1;
218 }
219
220 TCivetweb *engine = (TCivetweb *)request_info->user_data;
221 if (!engine || engine->IsTerminating())
222 return 1;
223 THttpServer *serv = engine->GetServer();
224 if (!serv)
225 return 1;
226
227 // this is continuation of the request
228 if (!fin) {
229 if (!conn_data) {
230 conn_data = new std::string(data,len);
232 } else {
233 conn_data->append(data,len);
234 }
235 return 1;
236 }
237
238 auto arg = std::make_shared<THttpCallArg>();
239 arg->SetPathAndFileName(request_info->local_uri); // path and file name
240 arg->SetQuery(request_info->query_string); // query arguments
241 arg->SetTopName(engine->GetTopName());
242 arg->SetWSId(TString::Hash((void *)&conn, sizeof(void *)));
243 arg->SetMethod("WS_DATA");
244
245 if (conn_data) {
246 mg_set_user_connection_data(conn, nullptr);
247 conn_data->append(data,len);
248 arg->SetPostData(std::move(*conn_data));
249 delete conn_data;
250 } else {
251 arg->SetPostData(std::string(data,len));
252 }
253
254 serv->ExecuteWS(arg, kTRUE, kTRUE);
255
256 return 1;
257}
258
259
260//////////////////////////////////////////////////////////////////////////
261
262static int log_message_handler(const struct mg_connection *conn, const char *message)
263{
264 const struct mg_context *ctx = mg_get_context(conn);
265
266 TCivetweb *engine = (TCivetweb *)mg_get_user_data(ctx);
267
268 if (engine)
269 return engine->ProcessLog(message);
270
271 // provide debug output
272 if ((gDebug > 0) || strstr(message, "cannot bind to"))
273 fprintf(stderr, "Error in <TCivetweb::Log> %s\n", message);
274
275 return 0;
276}
277
281 {
282 fEngine = engine;
284 }
289};
290
291//////////////////////////////////////////////////////////////////////////
292/// Returns kTRUE in case of longpoll connection request - or at least looks like that
293
294Bool_t IsBadLongPollConnect(TCivetweb *engine, const std::shared_ptr<THttpCallArg> &arg)
295{
296 if (strcmp(arg->GetFileName(), "root.longpoll") != 0)
297 return kFALSE;
298
299 const char *q = arg->GetQuery();
300 if (!q || !*q)
301 return kFALSE;
302
303 if ((strstr(q, "raw_connect") != q) && (strstr(q, "txt_connect") != q))
304 return kFALSE;
305
306 return !CheckEngineThreads(engine, arg->GetPathName(), kTRUE);
307}
308
309//////////////////////////////////////////////////////////////////////////
310
311static int begin_request_handler(struct mg_connection *conn, void *)
312{
313 const struct mg_request_info *request_info = mg_get_request_info(conn);
314
315 TCivetweb *engine = (TCivetweb *)request_info->user_data;
316 if (!engine || engine->IsTerminating())
317 return 0;
318 THttpServer *serv = engine->GetServer();
319 if (!serv)
320 return 0;
321
323
324 auto arg = std::make_shared<THttpCallArg>();
325
327
328 Bool_t execres = kTRUE, debug = engine->IsDebugMode();
329
330 if (!debug && serv->IsFileRequested(request_info->local_uri, filename)) {
331 if ((filename.Length() > 3) && ((filename.Index(".js") != kNPOS) || (filename.Index(".css") != kNPOS))) {
332 std::string buf = THttpServer::ReadFileContent(filename.Data());
333 if (buf.empty()) {
334 arg->Set404();
335 } else {
336 arg->SetContentType(THttpServer::GetMimeType(filename.Data()));
337 arg->SetContent(std::move(buf));
338 if (engine->GetMaxAge() > 0)
339 arg->AddHeader("Cache-Control", TString::Format("max-age=%d", engine->GetMaxAge()));
340 else
341 arg->AddNoCacheHeader();
342 arg->SetZipping();
343 }
344 } else {
345 arg->SetFile(filename.Data());
346 }
347 } else {
348 arg->SetPathAndFileName(request_info->local_uri); // path and file name
349 arg->SetQuery(request_info->query_string); // query arguments
350 arg->SetTopName(engine->GetTopName());
351 arg->SetMethod(request_info->request_method); // method like GET or POST
352 if (request_info->remote_user)
353 arg->SetUserName(request_info->remote_user);
354
355 TString header;
356 for (int n = 0; n < request_info->num_headers; n++)
357 header.Append(
358 TString::Format("%s: %s\r\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
359 arg->SetRequestHeader(header);
360
361 const char *len = mg_get_header(conn, "Content-Length");
362 Int_t ilen = len ? TString(len).Atoi() : 0;
363
364 if (ilen > 0) {
365 std::string buf;
366 buf.resize(ilen);
367 Int_t iread = mg_read(conn, (void *) buf.data(), ilen);
368 if (iread == ilen)
369 arg->SetPostData(std::move(buf));
370 }
371
372 if (debug) {
374 cont.Append("<title>Civetweb echo</title>");
375 cont.Append("<h1>Civetweb echo</h1>\n");
376
377 static int count = 0;
378
379 cont.Append(TString::Format("Request %d:<br/>\n<pre>\n", ++count));
380 cont.Append(TString::Format(" Method : %s\n", arg->GetMethod()));
381 cont.Append(TString::Format(" PathName : %s\n", arg->GetPathName()));
382 cont.Append(TString::Format(" FileName : %s\n", arg->GetFileName()));
383 cont.Append(TString::Format(" Query : %s\n", arg->GetQuery()));
384 cont.Append(TString::Format(" PostData : %ld\n", arg->GetPostDataLength()));
385 if (arg->GetUserName())
386 cont.Append(TString::Format(" User : %s\n", arg->GetUserName()));
387
388 cont.Append("</pre><p>\n");
389
390 cont.Append("Environment:<br/>\n<pre>\n");
391 for (int n = 0; n < request_info->num_headers; n++)
392 cont.Append(
393 TString::Format(" %s = %s\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
394 cont.Append("</pre><p>\n");
395
396 arg->SetContentType("text/html");
397
398 arg->SetContent(cont);
399
400 } else if (IsBadLongPollConnect(engine, arg)) {
401 execres = kFALSE;
402 arg->Set404();
403 } else {
404 execres = serv->ExecuteHttp(arg);
405 }
406 }
407
408 if (!execres || arg->Is404()) {
409 std::string hdr = arg->FillHttpHeader("HTTP/1.1");
410 mg_printf(conn, "%s", hdr.c_str());
411 } else if (arg->IsFile()) {
412 filename = (const char *)arg->GetContent();
413#ifdef _MSC_VER
414 if (engine->IsWinSymLinks()) {
415 // resolve Windows links which are not supported by civetweb
416 auto hFile = CreateFile(filename.Data(), // file to open
417 GENERIC_READ, // open for reading
418 FILE_SHARE_READ, // share for reading
419 NULL, // default security
420 OPEN_EXISTING, // existing file only
421 FILE_ATTRIBUTE_NORMAL, // normal file
422 NULL); // no attr. template
423
425 const int BUFSIZE = 2048;
428 // produced file name may include \\?\ symbols, which are indicating long file name
429 if(dwRet < BUFSIZE) {
430 if (dwRet > 4 && Path[0] == '\\' && Path[1] == '\\' && Path[2] == '?' && Path[3] == '\\')
431 filename = Path + 4;
432 else
433 filename = Path;
434 }
436 }
437 }
438#endif
439 const char *mime_type = THttpServer::GetMimeType(filename.Data());
440 if (mime_type)
441 mg_send_mime_file(conn, filename.Data(), mime_type);
442 else
443 mg_send_file(conn, filename.Data());
444 } else {
445
447 switch (arg->GetZipping()) {
448 case THttpCallArg::kNoZip: dozip = kFALSE; break;
450 if (arg->GetContentLength() < 10000) break;
452 // check if request header has Accept-Encoding
453 for (int n = 0; n < request_info->num_headers; n++) {
454 TString name = request_info->http_headers[n].name;
455 if (name.Index("Accept-Encoding", 0, TString::kIgnoreCase) != 0)
456 continue;
457 TString value = request_info->http_headers[n].value;
458 dozip = (value.Index("gzip", 0, TString::kIgnoreCase) != kNPOS);
459 break;
460 }
461
462 break;
463 case THttpCallArg::kZipAlways: dozip = kTRUE; break;
464 }
465
466 #ifdef _EXTERNAL_CIVETWEB
467 // with external civeweb one gets failure R__memcompress
468 // while it is not critical, try to avoid for now
469 // to be tested later
470 (void) dozip;
471 #else
472 if (dozip)
473 arg->CompressWithGzip();
474 #endif
475
476 std::string hdr = arg->FillHttpHeader("HTTP/1.1");
477 mg_printf(conn, "%s", hdr.c_str());
478
479 if (arg->IsChunked()) {
480 // send first portion
481
482 unsigned last_send = arg->GetContentLength();
483 mg_send_chunk(conn, (const char *)arg->GetContent(), last_send);
484
485 while (arg->IsChunked() && last_send) {
486 // loop
487 arg->SetContent("");
488
489 serv->ExecuteHttp(arg);
490
491 last_send = arg->GetContentLength();
492
493 mg_send_chunk(conn, (const char *)arg->GetContent(), last_send);
494 }
495
496 // to correctly complete chunk operation, send 0 buffer at the end
497 if (last_send)
498 mg_send_chunk(conn, "", 0);
499
500 } else if (arg->GetContentLength() > 0)
501 mg_write(conn, arg->GetContent(), (size_t)arg->GetContentLength());
502 }
503
504 // Returning non-zero tells civetweb that our function has replied to
505 // the client, and civetweb should not send client any more data.
506 return 1;
507}
508
509/** \class TCivetweb
510\ingroup http
511
512THttpEngine implementation, based on civetweb embedded server
513
514It is default kind of engine, created for THttpServer
515Currently v1.15 from https://github.com/civetweb/civetweb is used
516
517Additional options can be specified:
518
519 top=foldername - name of top folder, seen in the browser
520 thrds=N - use N threads to run civetweb server (default 5)
521 auth_file - global authentication file
522 auth_domain - domain name, used for authentication
523
524Example:
525
526 new THttpServer("http:8080?top=MyApp&thrds=3");
527
528For the full list of supported options see TCivetweb::Create() documentation
529
530When `auth_file` and `auth_domain` parameters are specified, access
531to running http server will be possible only after user
532authentication, using so-call digest method. To generate
533authentication file, htdigest routine should be used:
534
535 [shell] htdigest -c .htdigest domain_name user
536
537When creating server, parameters should be:
538
539 auto serv = new THttpServer("http:8080?auth_file=.htdigets&auth_domain=domain_name");
540
541*/
542
543////////////////////////////////////////////////////////////////////////////////
544/// constructor
545
547 : THttpEngine("civetweb", "compact embedded http server"),
548 fOnlySecured(only_secured)
549{
550}
551
552////////////////////////////////////////////////////////////////////////////////
553/// destructor
554
556{
557 if (fCtx && !fTerminating)
558 mg_stop(fCtx);
559
561}
562
563////////////////////////////////////////////////////////////////////////////////
564/// process civetweb log message, can be used to detect critical errors
565
566Int_t TCivetweb::ProcessLog(const char *message)
567{
568 if ((gDebug > 0) || strstr(message, "cannot bind to"))
569 Error("Log", "%s", message);
570
571 return 0;
572}
573
574////////////////////////////////////////////////////////////////////////////////
575/// Returns number of actively used threads
576
578{
579 std::lock_guard<std::mutex> guard(fMutex);
581}
582
583////////////////////////////////////////////////////////////////////////////////
584/// Returns number of actively used threads
585
587{
588 std::lock_guard<std::mutex> guard(fMutex);
589 fNumActiveThreads += cnt;
590 return fNumActiveThreads;
591}
592
593
594////////////////////////////////////////////////////////////////////////////////
595/// Creates embedded civetweb server
596///
597/// @param args string with civetweb server configuration
598///
599/// As main argument, http port should be specified like "8090".
600/// Or one can provide combination of ipaddress and portnumber like "127.0.0.1:8090"
601/// Or one can specify unix socket name like "x/tmp/root.socket"
602/// Extra parameters like in URL string could be specified after '?' mark:
603///
604/// thrds=N - there N is number of threads used by the civetweb (default is 10)
605/// top=name - configure top name, visible in the web browser
606/// ssl_certificate=filename - SSL certificate, see docs/OpenSSL.md from civetweb
607/// auth_file=filename - authentication file name, created with htdigets utility
608/// auth_domain=domain - authentication domain
609/// websocket_timeout=tm - set web sockets timeout in seconds (default 300)
610/// websocket_disable - disable web sockets handling (default enabled)
611/// bind - ip address to bind server socket
612/// loopback - bind specified port to loopback 127.0.0.1 address
613/// debug - enable debug mode, server always returns html page with request info
614/// log=filename - configure civetweb log file
615/// max_age=value - configures "Cache-Control: max_age=value" http header for all file-related requests, default 3600
616/// socket_mode=value - configures unix socket mode, default is 0700
617/// nocache - try to fully disable cache control for file requests
618/// winsymlinks=no - do not resolve symbolic links on file system (Windows only), default true
619/// dirlisting=no - enable/disable directory listing for browsing filesystem (default no)
620///
621/// Examples of valid args values:
622///
623/// serv->CreateEngine("http:8080?websocket_disable");
624/// serv->CreateEngine("http:7546?thrds=30&websocket_timeout=20");
625
626Bool_t TCivetweb::Create(const char *args)
627{
628 memset(&fCallbacks, 0, sizeof(struct mg_callbacks));
629 // fCallbacks.begin_request = begin_request_handler;
630 fCallbacks.log_message = log_message_handler;
631 TString sport = IsSecured() ? "8480s" : "8080",
633 websocket_timeout = "300000",
634 dir_listening = "no",
635 auth_file,
637 log_file,
638 ssl_cert,
639 max_age;
640 Int_t socket_mode = 0700;
641 bool use_ws = kTRUE, is_socket = false;
642
643 // extract arguments
644 if (args && *args) {
645
646 // first extract port number
647 sport = "";
648
649 is_socket = *args == 'x';
650
651 while ((*args != 0) && (*args != '?') && (is_socket || (*args != '/')))
652 sport.Append(*args++);
653 if (IsSecured() && (sport.Index("s") == kNPOS) && !is_socket)
654 sport.Append("s");
655
656 // than search for extra parameters
657 while ((*args != 0) && (*args != '?'))
658 args++;
659
660 if (*args == '?') {
661 TUrl url(TString::Format("http://localhost/folder%s", args));
662
663 if (url.IsValid()) {
664 url.ParseOptions();
665
666 fWebGui = url.HasOption("webgui");
667
668 const char *top = url.GetValueFromOptions("top");
669 if (top)
670 fTopName = top;
671
672 const char *log = url.GetValueFromOptions("log");
673 if (log)
674 log_file = log;
675
676 Int_t thrds = url.GetIntValueFromOptions("thrds");
677 if (thrds > 0)
679
680 const char *afile = url.GetValueFromOptions("auth_file");
681 if (afile)
683
684 const char *adomain = url.GetValueFromOptions("auth_domain");
685 if (adomain)
687
688 const char *sslc = url.GetValueFromOptions("ssl_cert");
689 if (sslc)
690 ssl_cert = sslc;
691
692 Int_t wtmout = url.GetIntValueFromOptions("websocket_timeout");
693 if (wtmout > 0) {
694 websocket_timeout.Format("%d", wtmout * 1000);
695 use_ws = kTRUE;
696 }
697
698 if (url.HasOption("websocket_disable"))
699 use_ws = kFALSE;
700
701 if (url.HasOption("debug"))
702 fDebug = kTRUE;
703
704 const char *winsymlinks = url.GetValueFromOptions("winsymlinks");
705 if (winsymlinks)
706 fWinSymLinks = strcmp(winsymlinks,"no") != 0;
707
708 const char *dls = url.GetValueFromOptions("dirlisting");
709 if (dls && (!strcmp(dls,"no") || !strcmp(dls,"yes")))
711
712 const char *smode = url.GetValueFromOptions("socket_mode");
713 if (smode)
714 socket_mode = std::stoi(smode, nullptr, *smode=='0' ? 8 : 10);
715
716 if (url.HasOption("loopback") && (sport.Index(":") == kNPOS))
717 sport = TString("127.0.0.1:") + sport;
718
719 if (url.HasOption("bind") && (sport.Index(":") == kNPOS)) {
720 const char *addr = url.GetValueFromOptions("bind");
721 if (addr && strlen(addr))
722 sport = TString(addr) + ":" + sport;
723 }
724
725 if (GetServer() && url.HasOption("cors")) {
726 const char *cors = url.GetValueFromOptions("cors");
727 GetServer()->SetCors(cors && *cors ? cors : "*");
728 }
729
730 if (GetServer() && url.HasOption("cred_cors")) {
731 const char *cred = url.GetValueFromOptions("cred_cors");
732 GetServer()->SetCorsCredentials(cred && *cred ? cred : "true");
733 }
734
735 if (url.HasOption("nocache"))
736 fMaxAge = 0;
737
738 if (url.HasOption("max_age"))
739 fMaxAge = url.GetIntValueFromOptions("max_age");
740
741 max_age.Form("%d", fMaxAge);
742 }
743 }
744 }
745
746 num_threads.Form("%d", fNumThreads);
747
748 const char *options[34];
749 int op = 0;
750
751 Info("Create", "Starting HTTP server on port %s", sport.Data());
752
753 options[op++] = "listening_ports";
754 options[op++] = sport.Data();
755 options[op++] = "num_threads";
756 options[op++] = num_threads.Data();
757
758 if (use_ws) {
759 options[op++] = "websocket_timeout_ms";
760 options[op++] = websocket_timeout.Data();
761 }
762
763 if ((auth_file.Length() > 0) && (auth_domain.Length() > 0)) {
764 options[op++] = "global_auth_file";
765 options[op++] = auth_file.Data();
766 options[op++] = "authentication_domain";
767 options[op++] = auth_domain.Data();
768 } else {
769 options[op++] = "enable_auth_domain_check";
770 options[op++] = "no";
771 }
772
773 if (log_file.Length() > 0) {
774 options[op++] = "error_log_file";
775 options[op++] = log_file.Data();
776 }
777
778 if (ssl_cert.Length() > 0) {
779 options[op++] = "ssl_certificate";
780 options[op++] = ssl_cert.Data();
781 } else if (IsSecured()) {
782 Error("Create", "No SSL certificate file configured");
783 }
784
785 if (max_age.Length() > 0) {
786 options[op++] = "static_file_max_age";
787 options[op++] = max_age.Data();
788 }
789
790 if (GetServer() && GetServer()->IsCors()) {
791 // also used for the file transfer
792 options[op++] = "access_control_allow_origin";
793 options[op++] = GetServer()->GetCors();
794 }
795
796 if (GetServer() && GetServer()->IsCorsCredentials()) {
797 options[op++] = "access_control_allow_credentials";
798 options[op++] = GetServer()->GetCorsCredentials();
799 options[op++] = "access_control_expose_headers";
800 options[op++] = "Content-Range, Content-Length, Date";
801 options[op++] = "access_control_allow_methods";
802 options[op++] = "GET, HEAD, OPTIONS";
803 }
804
805 options[op++] = "enable_directory_listing";
806 options[op++] = dir_listening.Data();
807
808 options[op++] = nullptr;
809
811 Error("Create", "civetweb compiled without sockets binding support");
812 return kFALSE;
813 }
814
816 Error("Create", "civetweb compiled without SSL support");
817 return kFALSE;
818 }
819
821 Error("Create", "civetweb compiled without websockets support");
822 return kFALSE;
823 }
824
825 if (IsSecured()) {
826 /* Initialize with SSL support */
828 } else {
829 /* Initialize without SSL support */
831 }
832
833 // try to remove socket file - if any
834 if (is_socket && !sport.Contains(","))
835 gSystem->Unlink(sport.Data()+1);
836
837 // Start the web server.
838 fCtx = mg_start(&fCallbacks, this, options);
839
840 if (!fCtx)
841 return kFALSE;
842
844
845 if (use_ws)
848
849 // try to remove socket file - if any
850 if (is_socket && !sport.Contains(","))
851 gSystem->Chmod(sport.Data()+1, socket_mode);
852
853 return kTRUE;
854}
bool Bool_t
Boolean (0=false, 1=true) (bool)
Definition RtypesCore.h:77
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:59
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
constexpr Ssiz_t kNPOS
The equivalent of std::string::npos for the ROOT class TString.
Definition RtypesCore.h:131
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
int websocket_connect_handler(const struct mg_connection *conn, void *)
Definition TCivetweb.cxx:98
static int begin_request_handler(struct mg_connection *conn, void *)
static int log_message_handler(const struct mg_connection *conn, const char *message)
int websocket_data_handler(struct mg_connection *conn, int code, char *data, size_t len, void *)
void websocket_close_handler(const struct mg_connection *conn, void *)
Bool_t CheckEngineThreads(TCivetweb *engine, const char *uri, Bool_t longpoll)
Check if engine has enough threads to process connect to new websocket handle.
Definition TCivetweb.cxx:81
void websocket_ready_handler(struct mg_connection *conn, void *)
Bool_t IsBadLongPollConnect(TCivetweb *engine, const std::shared_ptr< THttpCallArg > &arg)
Returns kTRUE in case of longpoll connection request - or at least looks like that.
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
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 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
char name[80]
Definition TGX11.cxx:110
float * q
#define INVALID_HANDLE_VALUE
Definition TMapFile.cxx:84
Int_t gDebug
Global variable setting the debug level. Set to 0 to disable, increase it in steps of 1 to increase t...
Definition TROOT.cxx:627
R__EXTERN TSystem * gSystem
Definition TSystem.h:572
#define BUFSIZE
const char * mime_type
Definition civetweb.c:8517
int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition civetweb.c:7255
void mg_send_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:10821
unsigned mg_check_feature(unsigned feature)
Definition civetweb.c:22103
struct mg_context * mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options)
Definition civetweb.c:21878
void mg_set_websocket_handler(struct mg_context *ctx, const char *uri, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
Definition civetweb.c:14810
unsigned mg_init_library(unsigned features)
Definition civetweb.c:23030
void mg_send_mime_file(struct mg_connection *conn, const char *path, const char *mime_type)
Definition civetweb.c:10828
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition civetweb.c:3929
unsigned mg_exit_library(void)
Definition civetweb.c:23160
int mg_write(struct mg_connection *conn, const void *buf, size_t len)
Definition civetweb.c:7000
const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn)
Definition civetweb.c:3595
void mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
Definition civetweb.c:3297
void * mg_get_user_connection_data(const struct mg_connection *conn)
Definition civetweb.c:3310
int mg_send_chunk(struct mg_connection *conn, const char *chunk, unsigned int chunk_len)
Definition civetweb.c:7081
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6835
void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
Definition civetweb.c:14788
struct mg_context * mg_get_context(const struct mg_connection *conn)
Definition civetweb.c:3260
void mg_stop(struct mg_context *ctx)
Definition civetweb.c:20890
void * mg_get_user_data(const struct mg_context *ctx)
Definition civetweb.c:3267
@ MG_FEATURES_DEFAULT
Definition civetweb.h:57
@ MG_FEATURES_TLS
Definition civetweb.h:66
@ MG_FEATURES_X_DOMAIN_SOCKET
Definition civetweb.h:105
@ MG_FEATURES_SSL
Definition civetweb.h:67
@ MG_FEATURES_WEBSOCKET
Definition civetweb.h:79
int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
@ MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE
Definition civetweb.h:861
@ MG_WEBSOCKET_OPCODE_BINARY
Definition civetweb.h:860
@ MG_WEBSOCKET_OPCODE_TEXT
Definition civetweb.h:859
TCivetwebWSEngine.
Definition TCivetweb.cxx:32
struct mg_connection * fWSconn
Definition TCivetweb.cxx:34
UInt_t GetId() const override
Definition TCivetweb.cxx:44
void SendCharStar(const char *str) override
Envelope for sending string via the websocket.
Definition TCivetweb.cxx:71
void ClearHandle(Bool_t terminate) override
Definition TCivetweb.cxx:46
Bool_t SupportSendThrd() const override
True websocket requires extra thread to parallelize sending.
Definition TCivetweb.cxx:37
~TCivetwebWSEngine() override=default
void SendHeader(const char *hdr, const void *buf, int len) override
Special method to send binary data with text header For normal websocket it is two separated operatio...
Definition TCivetweb.cxx:63
void Send(const void *buf, int len) override
Definition TCivetweb.cxx:53
TCivetwebWSEngine(struct mg_connection *conn)
Definition TCivetweb.cxx:40
THttpEngine implementation, based on civetweb embedded server.
Definition TCivetweb.h:21
Int_t fNumThreads
! number of configured threads
Definition TCivetweb.h:25
Int_t fNumActiveThreads
! number of active threads - used in request and websocket handling
Definition TCivetweb.h:26
struct mg_context * fCtx
! civetweb context
Definition TCivetweb.h:23
std::mutex fMutex
! mutex to read/write fNumActiveThreads
Definition TCivetweb.h:27
Bool_t fWinSymLinks
! resolve symbolic links on Windows
Definition TCivetweb.h:34
Bool_t IsTerminating() const
Definition TCivetweb.h:58
Int_t GetMaxAge() const
Definition TCivetweb.h:64
const char * GetTopName() const
Definition TCivetweb.h:52
TCivetweb(Bool_t only_secured=kFALSE)
constructor
Int_t fMaxAge
! max-age parameter
Definition TCivetweb.h:33
Bool_t IsWebGui() const
Definition TCivetweb.h:54
Int_t ProcessLog(const char *message)
process civetweb log message, can be used to detect critical errors
TString fTopName
! name of top item
Definition TCivetweb.h:28
Int_t GetNumAvailableThreads()
Returns number of actively used threads.
Bool_t fTerminating
! server doing shutdown and not react on requests
Definition TCivetweb.h:31
Bool_t Create(const char *args) override
Creates embedded civetweb server.
Int_t ChangeNumActiveThrerads(int cnt=0)
Returns number of actively used threads.
Int_t GetNumThreads() const
Definition TCivetweb.h:46
Bool_t IsSecured() const
Definition TCivetweb.h:38
Bool_t IsDebugMode() const
Definition TCivetweb.h:56
virtual ~TCivetweb()
destructor
Bool_t IsWinSymLinks() const
Definition TCivetweb.h:60
Bool_t fWebGui
! if server used for webgui
Definition TCivetweb.h:29
Bool_t fDebug
! debug mode
Definition TCivetweb.h:30
struct mg_callbacks fCallbacks
! call-back table for civetweb webserver
Definition TCivetweb.h:24
Abstract class for implementing http protocol for THttpServer.
Definition THttpEngine.h:19
THttpServer * GetServer() const
Returns pointer to THttpServer associated with engine.
Definition THttpEngine.h:40
Online http server for arbitrary ROOT application.
Definition THttpServer.h:31
const char * GetCors() const
Returns specified CORS domain.
void SetCorsCredentials(const std::string &value="true")
Enable/disable usage Access-Control-Allow-Credentials response header.
void SetCors(const std::string &domain="*")
Enable CORS header to ProcessRequests() responses Specified location (typically "*") add as "Access-C...
const char * GetCorsCredentials() const
Returns specified CORS credentials value - if any.
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
Internal instance used to exchange WS functionality between THttpServer and THttpWSHandler.
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1072
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1046
Basic string class.
Definition TString.h:138
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1995
@ kIgnoreCase
Definition TString.h:285
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition TString.cxx:685
TString & Append(const char *cs)
Definition TString.h:580
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:2385
virtual int Chmod(const char *file, UInt_t mode)
Set the file permission bits. Returns -1 in case or error, 0 otherwise.
Definition TSystem.cxx:1521
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1394
This class represents a WWW compatible URL.
Definition TUrl.h:33
const Int_t n
Definition legend1.C:16
TCivetweb * fEngine
TEngineHolder(TCivetweb *engine)
struct mg_header http_headers[(64)]
Definition civetweb.h:179
const char * local_uri
Definition civetweb.h:157
void * user_data
Definition civetweb.h:175
const char * request_method
Definition civetweb.h:151
const char * query_string
Definition civetweb.h:162
void * conn_data
Definition civetweb.h:176
const char * remote_user
Definition civetweb.h:164