14 #include "../civetweb/civetweb.h"
38 class TCivetwebWSEngine :
public THttpWSEngine {
40 struct mg_connection *fWSconn;
43 Bool_t SupportSendThrd()
const override {
return kTRUE; }
46 TCivetwebWSEngine(
struct mg_connection *conn) : THttpWSEngine(), fWSconn(conn) {}
48 virtual ~TCivetwebWSEngine() =
default;
50 UInt_t GetId()
const override {
return TString::Hash((
void *)&fWSconn,
sizeof(
void *)); }
52 void ClearHandle(Bool_t terminate)
override
54 if (fWSconn && terminate)
55 mg_websocket_write(fWSconn, MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE,
nullptr, 0);
59 void Send(
const void *buf,
int len)
override
62 mg_websocket_write(fWSconn, MG_WEBSOCKET_OPCODE_BINARY, (
const char *)buf, len);
69 void SendHeader(
const char *hdr,
const void *buf,
int len)
override
72 mg_websocket_write(fWSconn, MG_WEBSOCKET_OPCODE_TEXT, hdr, strlen(hdr));
73 mg_websocket_write(fWSconn, MG_WEBSOCKET_OPCODE_BINARY, (
const char *)buf, len);
77 void SendCharStar(
const char *str)
override
80 mg_websocket_write(fWSconn, MG_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
86 int websocket_connect_handler(
const struct mg_connection *conn,
void *)
88 const struct mg_request_info *request_info = mg_get_request_info(conn);
92 TCivetweb *engine = (TCivetweb *)request_info->user_data;
93 if (!engine || engine->IsTerminating())
95 THttpServer *serv = engine->GetServer();
99 auto arg = std::make_shared<THttpCallArg>();
100 arg->SetPathAndFileName(request_info->local_uri);
101 arg->SetQuery(request_info->query_string);
102 arg->SetWSId(TString::Hash((
void *)&conn,
sizeof(
void *)));
103 arg->SetMethod(
"WS_CONNECT");
105 Bool_t execres = serv->ExecuteWS(arg, kTRUE, kTRUE);
107 return execres && !arg->Is404() ? 0 : 1;
112 void websocket_ready_handler(
struct mg_connection *conn,
void *)
114 const struct mg_request_info *request_info = mg_get_request_info(conn);
116 TCivetweb *engine = (TCivetweb *)request_info->user_data;
117 if (!engine || engine->IsTerminating())
119 THttpServer *serv = engine->GetServer();
123 auto arg = std::make_shared<THttpCallArg>();
124 arg->SetPathAndFileName(request_info->local_uri);
125 arg->SetQuery(request_info->query_string);
126 arg->SetMethod(
"WS_READY");
129 arg->CreateWSEngine<TCivetwebWSEngine>(conn);
131 serv->ExecuteWS(arg, kTRUE, kTRUE);
136 int websocket_data_handler(
struct mg_connection *conn,
int code,
char *data,
size_t len,
void *)
138 const struct mg_request_info *request_info = mg_get_request_info(conn);
144 TCivetweb *engine = (TCivetweb *)request_info->user_data;
145 if (!engine || engine->IsTerminating())
147 THttpServer *serv = engine->GetServer();
151 std::string *conn_data = (std::string *) mg_get_user_connection_data(conn);
154 if (!(code & 0x80)) {
156 conn_data =
new std::string(data,len);
157 mg_set_user_connection_data(conn, conn_data);
159 conn_data->append(data,len);
164 auto arg = std::make_shared<THttpCallArg>();
165 arg->SetPathAndFileName(request_info->local_uri);
166 arg->SetQuery(request_info->query_string);
167 arg->SetWSId(TString::Hash((
void *)&conn,
sizeof(
void *)));
168 arg->SetMethod(
"WS_DATA");
171 mg_set_user_connection_data(conn,
nullptr);
172 conn_data->append(data,len);
173 arg->SetPostData(std::move(*conn_data));
176 arg->SetPostData(std::string(data,len));
179 serv->ExecuteWS(arg, kTRUE, kTRUE);
186 void websocket_close_handler(
const struct mg_connection *conn,
void *)
188 const struct mg_request_info *request_info = mg_get_request_info(conn);
190 TCivetweb *engine = (TCivetweb *)request_info->user_data;
191 if (!engine || engine->IsTerminating())
193 THttpServer *serv = engine->GetServer();
197 auto arg = std::make_shared<THttpCallArg>();
198 arg->SetPathAndFileName(request_info->local_uri);
199 arg->SetQuery(request_info->query_string);
200 arg->SetWSId(TString::Hash((
void *)&conn,
sizeof(
void *)));
201 arg->SetMethod(
"WS_CLOSE");
203 serv->ExecuteWS(arg, kTRUE, kFALSE);
208 static int log_message_handler(
const struct mg_connection *conn,
const char *message)
210 const struct mg_context *ctx = mg_get_context(conn);
212 TCivetweb *engine = (TCivetweb *)mg_get_user_data(ctx);
215 return engine->ProcessLog(message);
218 if ((gDebug > 0) || (strstr(message,
"cannot bind to") != 0))
219 fprintf(stderr,
"Error in <TCivetweb::Log> %s\n", message);
226 static int begin_request_handler(
struct mg_connection *conn,
void *)
228 const struct mg_request_info *request_info = mg_get_request_info(conn);
230 TCivetweb *engine = (TCivetweb *)request_info->user_data;
231 if (!engine || engine->IsTerminating())
233 THttpServer *serv = engine->GetServer();
237 auto arg = std::make_shared<THttpCallArg>();
241 Bool_t execres = kTRUE, debug = engine->IsDebugMode();
243 if (!debug && serv->IsFileRequested(request_info->local_uri, filename)) {
244 if ((filename.Length() > 3) && ((filename.Index(
".js") != kNPOS) || (filename.Index(
".css") != kNPOS))) {
245 std::string buf = THttpServer::ReadFileContent(filename.Data());
249 arg->SetContentType(THttpServer::GetMimeType(filename.Data()));
250 arg->SetContent(std::move(buf));
251 if (engine->GetMaxAge() > 0)
252 arg->AddHeader(
"Cache-Control", TString::Format(
"max-age=%d", engine->GetMaxAge()));
254 arg->AddNoCacheHeader();
258 arg->SetFile(filename.Data());
261 arg->SetPathAndFileName(request_info->local_uri);
262 arg->SetQuery(request_info->query_string);
263 arg->SetTopName(engine->GetTopName());
264 arg->SetMethod(request_info->request_method);
265 if (request_info->remote_user)
266 arg->SetUserName(request_info->remote_user);
269 for (
int n = 0; n < request_info->num_headers; n++)
271 TString::Format(
"%s: %s\r\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
272 arg->SetRequestHeader(header);
274 const char *len = mg_get_header(conn,
"Content-Length");
275 Int_t ilen = len ? TString(len).Atoi() : 0;
280 Int_t iread = mg_read(conn, (
void *) buf.data(), ilen);
282 arg->SetPostData(std::move(buf));
287 cont.Append(
"<title>Civetweb echo</title>");
288 cont.Append(
"<h1>Civetweb echo</h1>\n");
290 static int count = 0;
292 cont.Append(TString::Format(
"Request %d:<br/>\n<pre>\n", ++count));
293 cont.Append(TString::Format(
" Method : %s\n", arg->GetMethod()));
294 cont.Append(TString::Format(
" PathName : %s\n", arg->GetPathName()));
295 cont.Append(TString::Format(
" FileName : %s\n", arg->GetFileName()));
296 cont.Append(TString::Format(
" Query : %s\n", arg->GetQuery()));
297 cont.Append(TString::Format(
" PostData : %ld\n", arg->GetPostDataLength()));
298 if (arg->GetUserName())
299 cont.Append(TString::Format(
" User : %s\n", arg->GetUserName()));
301 cont.Append(
"</pre><p>\n");
303 cont.Append(
"Environment:<br/>\n<pre>\n");
304 for (
int n = 0; n < request_info->num_headers; n++)
306 TString::Format(
" %s = %s\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
307 cont.Append(
"</pre><p>\n");
309 arg->SetContentType(
"text/html");
311 arg->SetContent(cont);
314 execres = serv->ExecuteHttp(arg);
318 if (!execres || arg->Is404()) {
319 std::string hdr = arg->FillHttpHeader(
"HTTP/1.1");
320 mg_printf(conn,
"%s", hdr.c_str());
321 }
else if (arg->IsFile()) {
322 filename = (
const char *)arg->GetContent();
325 const int BUFSIZE = 2048;
328 auto hFile = CreateFile(filename.Data(),
333 FILE_ATTRIBUTE_NORMAL,
336 if( hFile != INVALID_HANDLE_VALUE) {
337 auto dwRet = GetFinalPathNameByHandle( hFile, Path, BUFSIZE, VOLUME_NAME_DOS );
344 mg_send_file(conn, filename.Data());
347 Bool_t dozip = kFALSE;
348 switch (arg->GetZipping()) {
349 case THttpCallArg::kNoZip: dozip = kFALSE;
break;
350 case THttpCallArg::kZipLarge:
351 if (arg->GetContentLength() < 10000)
break;
352 case THttpCallArg::kZip:
354 for (
int n = 0; n < request_info->num_headers; n++) {
355 TString name = request_info->http_headers[n].name;
356 if (name.Index(
"Accept-Encoding", 0, TString::kIgnoreCase) != 0)
358 TString value = request_info->http_headers[n].value;
359 dozip = (value.Index(
"gzip", 0, TString::kIgnoreCase) != kNPOS);
364 case THttpCallArg::kZipAlways: dozip = kTRUE;
break;
368 arg->CompressWithGzip();
370 std::string hdr = arg->FillHttpHeader(
"HTTP/1.1");
371 mg_printf(conn,
"%s", hdr.c_str());
373 if (arg->GetContentLength() > 0)
374 mg_write(conn, arg->GetContent(), (size_t)arg->GetContentLength());
416 TCivetweb::TCivetweb(Bool_t only_secured)
417 : THttpEngine(
"civetweb",
"compact embedded http server"), fCtx(nullptr), fCallbacks(nullptr), fTopName(),
418 fDebug(kFALSE), fTerminating(kFALSE), fOnlySecured(only_secured)
425 TCivetweb::~TCivetweb()
427 if (fCtx && !fTerminating)
428 mg_stop((
struct mg_context *)fCtx);
436 Int_t TCivetweb::ProcessLog(
const char *message)
438 if ((gDebug > 0) || (strstr(message,
"cannot bind to") != 0))
439 Error(
"Log",
"%s", message);
466 Bool_t TCivetweb::Create(
const char *args)
468 fCallbacks = malloc(
sizeof(
struct mg_callbacks));
469 memset(fCallbacks, 0,
sizeof(
struct mg_callbacks));
471 ((
struct mg_callbacks *)fCallbacks)->log_message = log_message_handler;
472 TString sport = IsSecured() ?
"8480s" :
"8080", num_threads =
"10", websocket_timeout =
"300000";
473 TString auth_file, auth_domain, log_file, ssl_cert, max_age;
474 Bool_t use_ws = kTRUE;
477 if (args && (strlen(args) > 0)) {
481 while ((*args != 0) && (*args !=
'?') && (*args !=
'/'))
482 sport.Append(*args++);
483 if (IsSecured() && (sport.Index(
"s")==kNPOS)) sport.Append(
"s");
486 while ((*args != 0) && (*args !=
'?'))
490 TUrl url(TString::Format(
"http://localhost/folder%s", args));
495 const char *top = url.GetValueFromOptions(
"top");
499 const char *log = url.GetValueFromOptions(
"log");
503 Int_t thrds = url.GetIntValueFromOptions(
"thrds");
505 num_threads.Form(
"%d", thrds);
507 const char *afile = url.GetValueFromOptions(
"auth_file");
511 const char *adomain = url.GetValueFromOptions(
"auth_domain");
513 auth_domain = adomain;
515 const char *sslc = url.GetValueFromOptions(
"ssl_cert");
519 Int_t wtmout = url.GetIntValueFromOptions(
"websocket_timeout");
521 websocket_timeout.Format(
"%d", wtmout * 1000);
525 if (url.HasOption(
"websocket_disable"))
528 if (url.HasOption(
"debug"))
531 if (url.HasOption(
"loopback") && (sport.Index(
":") == kNPOS))
532 sport = TString(
"127.0.0.1:") + sport;
534 if (url.HasOption(
"bind") && (sport.Index(
":") == kNPOS)) {
535 const char *addr = url.GetValueFromOptions(
"bind");
536 if (addr && strlen(addr))
537 sport = TString(addr) +
":" + sport;
540 if (GetServer() && url.HasOption(
"cors")) {
541 const char *cors = url.GetValueFromOptions(
"cors");
542 GetServer()->SetCors(cors && *cors ? cors :
"*");
545 if (url.HasOption(
"nocache"))
548 if (url.HasOption(
"max_age"))
549 fMaxAge = url.GetIntValueFromOptions(
"max_age");
551 max_age.Form(
"%d", fMaxAge);
556 const char *options[20];
559 Info(
"Create",
"Starting HTTP server on port %s", sport.Data());
561 options[op++] =
"listening_ports";
562 options[op++] = sport.Data();
563 options[op++] =
"num_threads";
564 options[op++] = num_threads.Data();
567 options[op++] =
"websocket_timeout_ms";
568 options[op++] = websocket_timeout.Data();
571 if ((auth_file.Length() > 0) && (auth_domain.Length() > 0)) {
572 options[op++] =
"global_auth_file";
573 options[op++] = auth_file.Data();
574 options[op++] =
"authentication_domain";
575 options[op++] = auth_domain.Data();
578 if (log_file.Length() > 0) {
579 options[op++] =
"error_log_file";
580 options[op++] = log_file.Data();
583 if (ssl_cert.Length() > 0) {
584 options[op++] =
"ssl_certificate";
585 options[op++] = ssl_cert.Data();
586 }
else if (IsSecured()) {
587 Error(
"Create",
"No SSL certificate file configured");
590 if (max_age.Length() > 0) {
591 options[op++] =
"static_file_max_age";
592 options[op++] = max_age.Data();
595 options[op++] =
nullptr;
598 fCtx = mg_start((
struct mg_callbacks *)fCallbacks,
this, options);
603 mg_set_request_handler((
struct mg_context *)fCtx,
"/", begin_request_handler,
nullptr);
606 mg_set_websocket_handler((
struct mg_context *)fCtx,
"**root.websocket$", websocket_connect_handler,
607 websocket_ready_handler, websocket_data_handler, websocket_close_handler,
nullptr);