Logo ROOT   6.07/09
Reference Guide
TCivetweb.cxx
Go to the documentation of this file.
1 // $Id$
2 // Author: Sergey Linev 21/12/2013
3 
4 #include "TCivetweb.h"
5 
6 #include "../civetweb/civetweb.h"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "THttpServer.h"
12 #include "TUrl.h"
13 
14 
15 //////////////////////////////////////////////////////////////////////////
16 // //
17 // TCivetwebWSEngine //
18 // //
19 // Implementation of THttpWSEngine for Civetweb //
20 // //
21 //////////////////////////////////////////////////////////////////////////
22 
23 class TCivetwebWSEngine : public THttpWSEngine {
24  protected:
25 
26  struct mg_connection *fWSconn;
27 
28  public:
29 
30  TCivetwebWSEngine(const char* name, const char* title, struct mg_connection *conn) :
31  THttpWSEngine(name, title),
32  fWSconn(conn)
33  {
34  }
35 
36  virtual ~TCivetwebWSEngine()
37  {
38  }
39 
40  virtual void ClearHandle()
41  {
42  fWSconn = 0;
43  }
44 
45  virtual void Send(const void* buf, int len)
46  {
47  if (fWSconn)
48  mg_websocket_write(fWSconn, WEBSOCKET_OPCODE_TEXT, (const char*) buf, len);
49  }
50 
51 };
52 
53 
54 //////////////////////////////////////////////////////////////////////////
55 
56 int websocket_connect_handler(const struct mg_connection *conn, void*)
57 {
58  const struct mg_request_info *request_info = mg_get_request_info(conn);
59  if (request_info == 0) return 1;
60 
61  // printf("Request websocket for uri:%s\n", request_info->uri);
62 
63  TCivetweb *engine = (TCivetweb *) request_info->user_data;
64  if (engine == 0) return 1;
65  THttpServer *serv = engine->GetServer();
66  if (serv == 0) return 1;
67 
68  THttpCallArg arg;
69  arg.SetPathAndFileName(request_info->uri); // path and file name
70  arg.SetQuery(request_info->query_string); // query arguments
71  arg.SetMethod("WS_CONNECT");
72 
73  Bool_t execres = serv->ExecuteHttp(&arg);
74 
75  // printf("res %d 404 %d\n", execres, arg.Is404());
76 
77  return execres && !arg.Is404() ? 0 : 1;
78 }
79 
80 //////////////////////////////////////////////////////////////////////////
81 
82 void websocket_ready_handler(struct mg_connection *conn, void*)
83 {
84  const struct mg_request_info *request_info = mg_get_request_info(conn);
85 
86  // printf("Websocket connection established url:%s\n", request_info->uri);
87 
88  TCivetweb *engine = (TCivetweb *) request_info->user_data;
89  if (engine == 0) return;
90  THttpServer *serv = engine->GetServer();
91  if (serv == 0) return;
92 
93  THttpCallArg arg;
94  arg.SetPathAndFileName(request_info->uri); // path and file name
95  arg.SetQuery(request_info->query_string); // query arguments
96  arg.SetMethod("WS_READY");
97 
98  arg.SetWSHandle(new TCivetwebWSEngine("websocket", "title", conn));
99 
100  serv->ExecuteHttp(&arg);
101 }
102 
103 //////////////////////////////////////////////////////////////////////////
104 
105 int websocket_data_handler(struct mg_connection *conn, int, char *data, size_t len, void*)
106 {
107  const struct mg_request_info *request_info = mg_get_request_info(conn);
108 
109  TCivetweb *engine = (TCivetweb *) request_info->user_data;
110  if (engine == 0) return 1;
111  THttpServer *serv = engine->GetServer();
112  if (serv == 0) return 1;
113 
114  THttpCallArg arg;
115  arg.SetPathAndFileName(request_info->uri); // path and file name
116  arg.SetQuery(request_info->query_string); // query arguments
117  arg.SetMethod("WS_DATA");
118 
119  void* buf = malloc(len+1); // one byte more for null-termination
120  memcpy(buf, data, len);
121  arg.SetPostData(buf, len);
122 
123  //if ((bits & 0xF) == 1)
124  // printf("Get string len %d %s\n", (int) len, (char*) arg.GetPostData());
125  //else
126  // printf("Get data from web socket len bits %d %d\n", bits, (int) len);
127 
128  serv->ExecuteHttp(&arg);
129 
130  // static int wscnt = 0;
131  //if (++wscnt >= 20000) {
132  // const char* reply = "Send close message";
133  // mg_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, reply, strlen(reply));
134  //}
135 
136  return 1;
137 }
138 
139 //////////////////////////////////////////////////////////////////////////
140 
141 void websocket_close_handler(const struct mg_connection *conn, void*)
142 {
143  const struct mg_request_info *request_info = mg_get_request_info(conn);
144 
145  // printf("Websocket connection closed url:%s\n", request_info->uri);
146 
147  TCivetweb *engine = (TCivetweb *) request_info->user_data;
148  if (engine == 0) return;
149  THttpServer *serv = engine->GetServer();
150  if (serv == 0) return;
151 
152  THttpCallArg arg;
153  arg.SetPathAndFileName(request_info->uri); // path and file name
154  arg.SetQuery(request_info->query_string); // query arguments
155  arg.SetMethod("WS_CLOSE");
156 
157  serv->ExecuteHttp(&arg);
158 }
159 
160 
161 //////////////////////////////////////////////////////////////////////////
162 
163 static int log_message_handler(const struct mg_connection *conn, const char *message)
164 {
165  const struct mg_context *ctx = mg_get_context(conn);
166 
167  TCivetweb* engine = (TCivetweb*) mg_get_user_data(ctx);
168 
169  if (engine) return engine->ProcessLog(message);
170 
171  // provide debug output
172  if ((gDebug>0) || (strstr(message,"cannot bind to")!=0))
173  fprintf(stderr, "Error in <TCivetweb::Log> %s\n",message);
174 
175  return 0;
176 }
177 
178 
179 //////////////////////////////////////////////////////////////////////////
180 
181 static int begin_request_handler(struct mg_connection *conn, void*)
182 {
183  const struct mg_request_info *request_info = mg_get_request_info(conn);
184 
185  TCivetweb *engine = (TCivetweb *) request_info->user_data;
186  if (engine == 0) return 0;
187  THttpServer *serv = engine->GetServer();
188  if (serv == 0) return 0;
189 
190  THttpCallArg arg;
191 
192  TString filename;
193 
194  Bool_t execres = kTRUE, debug = engine->IsDebugMode();
195 
196  if (!debug && serv->IsFileRequested(request_info->uri, filename)) {
197  if ((filename.Index(".js") != kNPOS) || (filename.Index(".css") != kNPOS)) {
198  Int_t length = 0;
199  char *buf = THttpServer::ReadFileContent(filename.Data(), length);
200  if (buf == 0) {
201  arg.Set404();
202  } else {
204  arg.SetBinData(buf, length);
205  arg.AddHeader("Cache-Control", "max-age=3600");
206  arg.SetZipping(2);
207  }
208  } else {
209  arg.SetFile(filename.Data());
210  }
211  } else {
212  arg.SetPathAndFileName(request_info->uri); // path and file name
213  arg.SetQuery(request_info->query_string); // query arguments
214  arg.SetTopName(engine->GetTopName());
215  arg.SetMethod(request_info->request_method); // method like GET or POST
216  if (request_info->remote_user!=0)
217  arg.SetUserName(request_info->remote_user);
218 
219  TString header;
220  for (int n = 0; n < request_info->num_headers; n++)
221  header.Append(TString::Format("%s: %s\r\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
222  arg.SetRequestHeader(header);
223 
224  const char* len = mg_get_header(conn, "Content-Length");
225  Int_t ilen = len!=0 ? TString(len).Atoi() : 0;
226 
227  if (ilen>0) {
228  void* buf = malloc(ilen+1); // one byte more for null-termination
229  Int_t iread = mg_read(conn, buf, ilen);
230  if (iread==ilen) arg.SetPostData(buf, ilen);
231  else free(buf);
232  }
233 
234  if (debug) {
235  TString cont;
236  cont.Append("<title>Civetweb echo</title>");
237  cont.Append("<h1>Civetweb echo</h1>\n");
238 
239  static int count = 0;
240 
241  cont.Append(TString::Format("Request %d:<br/>\n<pre>\n", ++count));
242  cont.Append(TString::Format(" Method : %s\n", arg.GetMethod()));
243  cont.Append(TString::Format(" PathName : %s\n", arg.GetPathName()));
244  cont.Append(TString::Format(" FileName : %s\n", arg.GetFileName()));
245  cont.Append(TString::Format(" Query : %s\n", arg.GetQuery()));
246  cont.Append(TString::Format(" PostData : %ld\n", arg.GetPostDataLength()));
247  if (arg.GetUserName())
248  cont.Append(TString::Format(" User : %s\n", arg.GetUserName()));
249 
250  cont.Append("</pre><p>\n");
251 
252  cont.Append("Environment:<br/>\n<pre>\n");
253  for (int n = 0; n < request_info->num_headers; n++)
254  cont.Append(TString::Format(" %s = %s\n", request_info->http_headers[n].name, request_info->http_headers[n].value));
255  cont.Append("</pre><p>\n");
256 
257  arg.SetContentType("text/html");
258 
259  arg.SetContent(cont);
260 
261  } else {
262  execres = serv->ExecuteHttp(&arg);
263  }
264  }
265 
266  if (!execres || arg.Is404()) {
267  TString hdr;
268  arg.FillHttpHeader(hdr, "HTTP/1.1");
269  mg_printf(conn, "%s", hdr.Data());
270  } else if (arg.IsFile()) {
271  mg_send_file(conn, (const char *) arg.GetContent());
272  } else {
273 
274  Bool_t dozip = arg.GetZipping() > 0;
275  switch (arg.GetZipping()) {
276  case 2:
277  if (arg.GetContentLength() < 10000) {
278  dozip = kFALSE;
279  break;
280  }
281  case 1:
282  // check if request header has Accept-Encoding
283  dozip = kFALSE;
284  for (int n = 0; n < request_info->num_headers; n++) {
285  TString name = request_info->http_headers[n].name;
286  if (name.Index("Accept-Encoding", 0, TString::kIgnoreCase) != 0) continue;
287  TString value = request_info->http_headers[n].value;
288  dozip = (value.Index("gzip", 0, TString::kIgnoreCase) != kNPOS);
289  break;
290  }
291 
292  break;
293  case 3:
294  dozip = kTRUE;
295  break;
296  }
297 
298  if (dozip) arg.CompressWithGzip();
299 
300  TString hdr;
301  arg.FillHttpHeader(hdr, "HTTP/1.1");
302  mg_printf(conn, "%s", hdr.Data());
303 
304  if (arg.GetContentLength() > 0)
305  mg_write(conn, arg.GetContent(), (size_t) arg.GetContentLength());
306  }
307 
308  // Returning non-zero tells civetweb that our function has replied to
309  // the client, and civetweb should not send client any more data.
310  return 1;
311 }
312 
313 
314 //////////////////////////////////////////////////////////////////////////
315 // //
316 // TCivetweb //
317 // //
318 // http server implementation, based on civetweb embedded server //
319 // It is default kind of engine, created for THttpServer //
320 // //
321 // Following additional options can be specified //
322 // top=foldername - name of top folder, seen in the browser //
323 // thrds=N - use N threads to run civetweb server (default 5) //
324 // auth_file - global authentication file //
325 // auth_domain - domain name, used for authentication //
326 // //
327 // Example: //
328 // new THttpServer("http:8080?top=MyApp&thrds=3"); //
329 // //
330 // Authentication: //
331 // When auth_file and auth_domain parameters are specified, access //
332 // to running http server will be possible only after user //
333 // authentication, using so-call digest method. To generate //
334 // authentication file, htdigest routine should be used: //
335 // //
336 // [shell] htdigest -c .htdigest domain_name user //
337 // //
338 // When creating server, parameters should be: //
339 // //
340 // new THttpServer("http:8080?auth_file=.htdigets&auth_domain=domain_name"); //
341 // //
342 //////////////////////////////////////////////////////////////////////////
343 
344 
346 
347 ////////////////////////////////////////////////////////////////////////////////
348 /// constructor
349 
351  THttpEngine("civetweb", "compact embedded http server"),
352  fCtx(0),
353  fCallbacks(0),
354  fTopName(),
355  fDebug(kFALSE)
356 {
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////////
360 /// destructor
361 
363 {
364  if (fCtx != 0) mg_stop((struct mg_context *) fCtx);
365  if (fCallbacks != 0) free(fCallbacks);
366  fCtx = 0;
367  fCallbacks = 0;
368 }
369 
370 ////////////////////////////////////////////////////////////////////////////////
371 /// process civetweb log message, can be used to detect critical errors
372 
373 Int_t TCivetweb::ProcessLog(const char* message)
374 {
375  if ((gDebug>0) || (strstr(message,"cannot bind to")!=0)) Error("Log", "%s", message);
376 
377  return 0;
378 }
379 
380 ////////////////////////////////////////////////////////////////////////////////
381 /// Creates embedded civetweb server
382 /// As main argument, http port should be specified like "8090".
383 /// Or one can provide combination of ipaddress and portnumber like 127.0.0.1:8090
384 /// Extra parameters like in URL string could be specified after '?' mark:
385 /// thrds=N - there N is number of threads used by the civetweb (default is 5)
386 /// top=name - configure top name, visible in the web browser
387 /// auth_file=filename - authentication file name, created with htdigets utility
388 /// auth_domain=domain - authentication domain
389 /// loopback - bind specified port to loopback 127.0.0.1 address
390 /// debug - enable debug mode, server always returns html page with request info
391 
392 Bool_t TCivetweb::Create(const char *args)
393 {
394  fCallbacks = malloc(sizeof(struct mg_callbacks));
395  memset(fCallbacks, 0, sizeof(struct mg_callbacks));
396  //((struct mg_callbacks *) fCallbacks)->begin_request = begin_request_handler;
397  ((struct mg_callbacks *) fCallbacks)->log_message = log_message_handler;
398  TString sport = "8080";
399  TString num_threads = "5";
400  TString auth_file, auth_domain, log_file;
401 
402  // extract arguments
403  if ((args != 0) && (strlen(args) > 0)) {
404 
405  // first extract port number
406  sport = "";
407  while ((*args != 0) && (*args != '?') && (*args != '/'))
408  sport.Append(*args++);
409 
410  // than search for extra parameters
411  while ((*args != 0) && (*args != '?')) args++;
412 
413  if (*args == '?') {
414  TUrl url(TString::Format("http://localhost/folder%s", args));
415 
416  if (url.IsValid()) {
417  url.ParseOptions();
418 
419  const char *top = url.GetValueFromOptions("top");
420  if (top != 0) fTopName = top;
421 
422  const char *log = url.GetValueFromOptions("log");
423  if (log != 0) log_file = log;
424 
425  Int_t thrds = url.GetIntValueFromOptions("thrds");
426  if (thrds > 0) num_threads.Form("%d", thrds);
427 
428  const char *afile = url.GetValueFromOptions("auth_file");
429  if (afile != 0) auth_file = afile;
430 
431  const char *adomain = url.GetValueFromOptions("auth_domain");
432  if (adomain != 0) auth_domain = adomain;
433 
434  if (url.HasOption("debug")) fDebug = kTRUE;
435 
436  if (url.HasOption("loopback") && (sport.Index(":")==kNPOS))
437  sport = TString("127.0.0.1:") + sport;
438  }
439  }
440  }
441 
442  const char *options[20];
443  int op(0);
444 
445  Info("Create", "Starting HTTP server on port %s", sport.Data());
446 
447  options[op++] = "listening_ports";
448  options[op++] = sport.Data();
449  options[op++] = "num_threads";
450  options[op++] = num_threads.Data();
451 
452  if ((auth_file.Length() > 0) && (auth_domain.Length() > 0)) {
453  options[op++] = "global_auth_file";
454  options[op++] = auth_file.Data();
455  options[op++] = "authentication_domain";
456  options[op++] = auth_domain.Data();
457  }
458 
459  if (log_file.Length() > 0) {
460  options[op++] = "error_log_file";
461  options[op++] = log_file.Data();
462  }
463 
464  options[op++] = 0;
465 
466  // Start the web server.
467  fCtx = mg_start((struct mg_callbacks *) fCallbacks, this, options);
468 
469  if (fCtx == 0) return kFALSE;
470 
471  mg_set_request_handler((struct mg_context *) fCtx, "/", begin_request_handler, 0);
472 
473  mg_set_websocket_handler((struct mg_context *) fCtx,
474  "**root.websocket$",
479  0);
480 
481  return kTRUE;
482 }
483 
void SetZipping(Int_t kind)
Definition: THttpCallArg.h:277
const char * remote_user
Definition: civetweb.h:68
THttpServer * GetServer() const
Definition: THttpEngine.h:41
void SetRequestHeader(const char *h)
Definition: THttpCallArg.h:114
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
Definition: TUrl.cxx:672
const char * uri
Definition: civetweb.h:64
void websocket_ready_handler(struct mg_connection *conn, void *)
Definition: TCivetweb.cxx:82
void SetWSHandle(TNamed *handle)
assign websocket handle with HTTP call
Ssiz_t Length() const
Definition: TString.h:390
This class represents a WWW compatible URL.
Definition: TUrl.h:41
int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition: civetweb.c:4146
Long_t GetContentLength() const
Definition: THttpCallArg.h:327
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:899
Bool_t Is404() const
Definition: THttpCallArg.h:310
void mg_stop(struct mg_context *ctx)
Definition: civetweb.c:12800
const char * GetPathName() const
Definition: THttpCallArg.h:177
Basic string class.
Definition: TString.h:137
virtual Bool_t Create(const char *args)
Creates embedded civetweb server As main argument, http port should be specified like "8090"...
Definition: TCivetweb.cxx:392
void SetQuery(const char *q)
Definition: THttpCallArg.h:101
int Int_t
Definition: RtypesCore.h:41
struct mg_request_info::mg_header http_headers[64]
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kFALSE
Definition: Rtypes.h:92
virtual void Send(const void *buf, int len)=0
#define malloc
Definition: civetweb.c:818
void SetContentType(const char *typ)
Definition: THttpCallArg.h:207
const void * GetContent() const
Definition: THttpCallArg.h:332
void SetUserName(const char *n)
Definition: THttpCallArg.h:94
void SetContent(const char *c)
Definition: THttpCallArg.h:268
const char * GetMethod() const
Definition: THttpCallArg.h:149
void SetTopName(const char *topname)
Definition: THttpCallArg.h:71
const char * Data() const
Definition: TString.h:349
void websocket_close_handler(const struct mg_connection *conn, void *)
Definition: TCivetweb.cxx:141
const char * request_method
Definition: civetweb.h:58
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
void SetPostData(void *data, Long_t length)
set data, posted with the request buffer should be allocated with malloc(length+1) call...
void SetPathAndFileName(const char *fullpath)
set complete path of requested http element For instance, it could be "/folder/subfolder/get.bin" Here "/folder/subfolder/" is element path and "get.bin" requested file.
virtual ~TCivetweb()
destructor
Definition: TCivetweb.cxx:362
void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
Definition: civetweb.c:9566
Bool_t IsFile() const
Definition: THttpCallArg.h:315
const char * GetTopName() const
Definition: TCivetweb.h:24
Int_t ProcessLog(const char *message)
process civetweb log message, can be used to detect critical errors
Definition: TCivetweb.cxx:373
int websocket_connect_handler(const struct mg_connection *conn, void *)
Definition: TCivetweb.cxx:56
TString & Append(const char *cs)
Definition: TString.h:492
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...
Definition: TUrl.cxx:661
Int_t Atoi() const
Return integer value of string.
Definition: TString.cxx:1965
void mg_send_file(struct mg_connection *conn, const char *path)
Definition: civetweb.c:6674
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:925
const char * GetQuery() const
Definition: THttpCallArg.h:198
Bool_t IsValid() const
Definition: TUrl.h:88
int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition: civetweb.c:4400
Bool_t IsDebugMode() const
Definition: TCivetweb.h:29
void SetBinData(void *data, Long_t length)
set binary data, which will be returned as reply body
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
Definition: TUrl.cxx:649
const char * GetFileName() const
Definition: THttpCallArg.h:184
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2322
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
const char * GetUserName() const
Definition: THttpCallArg.h:191
struct mg_context * mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options)
Definition: civetweb.c:12875
int mg_write(struct mg_connection *conn, const void *buf, size_t len)
Definition: civetweb.c:4235
Bool_t ExecuteHttp(THttpCallArg *arg)
Execute HTTP request.
#define ClassImp(name)
Definition: Rtypes.h:279
const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition: civetweb.c:2063
static int log_message_handler(const struct mg_connection *conn, const char *message)
Definition: TCivetweb.cxx:163
CIVETWEB_API int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
void * mg_get_user_data(const struct mg_context *ctx)
Definition: civetweb.c:1755
void SetMethod(const char *method)
Definition: THttpCallArg.h:64
#define free
Definition: civetweb.c:821
Int_t GetZipping() const
Definition: THttpCallArg.h:288
void * user_data
Definition: civetweb.h:81
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
int websocket_data_handler(struct mg_connection *conn, int, char *data, size_t len, void *)
Definition: TCivetweb.cxx:105
const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn)
Definition: civetweb.c:1972
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
void SetFile(const char *filename=0)
Definition: THttpCallArg.h:221
void ParseOptions() const
Parse URL options into a key/value map.
Definition: TUrl.cxx:618
R__EXTERN Int_t gDebug
Definition: Rtypes.h:128
bool debug
virtual void ClearHandle()=0
Long_t GetPostDataLength() const
Definition: THttpCallArg.h:170
const char * query_string
Definition: civetweb.h:66
static int begin_request_handler(struct mg_connection *conn, void *)
Definition: TCivetweb.cxx:181
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:582
const Bool_t kTRUE
Definition: Rtypes.h:91
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:9586
const Int_t n
Definition: legend1.C:16
char name[80]
Definition: TGX11.cxx:109
double log(double)
struct mg_context * mg_get_context(const struct mg_connection *conn)
Definition: civetweb.c:1748
Bool_t CompressWithGzip()
compress reply data with gzip compression
if(line.BeginsWith("/*"))
Definition: HLFactory.cxx:443
void FillHttpHeader(TString &buf, const char *header=0)
fill HTTP header