Logo ROOT   6.07/09
Reference Guide
TFastCgi.cxx
Go to the documentation of this file.
1 // $Id$
2 // Author: Sergey Linev 28/12/2013
3 
4 #include "TFastCgi.h"
5 
6 #include "TThread.h"
7 #include "TUrl.h"
8 #include "THttpServer.h"
9 
10 #include <string.h>
11 
12 #ifdef WIN32
13 #include <io.h>
14 #else
15 #include <unistd.h>
16 #endif
17 
18 #ifndef HTTP_WITHOUT_FASTCGI
19 
20 #include "fcgiapp.h"
21 
22 #include <stdlib.h>
23 
24 void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
25 {
26  Int_t length = 0;
27 
28  char *buf = THttpServer::ReadFileContent(fname, length);
29 
30  if (buf == 0) {
31  FCGX_FPrintF(request->out,
32  "Status: 404 Not Found\r\n"
33  "Content-Length: 0\r\n" // Always set Content-Length
34  "Connection: close\r\n\r\n");
35  } else {
36 
37  FCGX_FPrintF(request->out,
38  "Status: 200 OK\r\n"
39  "Content-Type: %s\r\n"
40  "Content-Length: %d\r\n" // Always set Content-Length
41  "\r\n", THttpServer::GetMimeType(fname), length);
42 
43 
44  FCGX_PutStr(buf, length, request->out);
45 
46  free(buf);
47  }
48 }
49 
50 
51 #endif
52 
53 
54 //////////////////////////////////////////////////////////////////////////
55 // //
56 // TFastCgi //
57 // //
58 // http engine implementation, based on fastcgi package //
59 // Allows to redirect http requests from normal web server like //
60 // Apache or lighttpd //
61 // //
62 // Configuration example for lighttpd //
63 // //
64 // server.modules += ( "mod_fastcgi" ) //
65 // fastcgi.server = ( //
66 // "/remote_scripts/" => //
67 // (( "host" => "192.168.1.11", //
68 // "port" => 9000, //
69 // "check-local" => "disable", //
70 // "docroot" => "/" //
71 // )) //
72 // ) //
73 // //
74 // When creating THttpServer, one should specify: //
75 // //
76 // THttpServer* serv = new THttpServer("fastcgi:9000"); //
77 // //
78 // In this case, requests to lighttpd server will be //
79 // redirected to ROOT session. Like: //
80 // http://lighttpdhost/remote_scripts/root.cgi/ //
81 // //
82 // Following additional options can be specified //
83 // top=foldername - name of top folder, seen in the browser //
84 // debug=1 - run fastcgi server in debug mode //
85 // Example: //
86 // serv->CreateEngine("fastcgi:9000?top=fastcgiserver"); //
87 // //
88 // //
89 //////////////////////////////////////////////////////////////////////////
90 
91 
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// normal constructor
96 
98  THttpEngine("fastcgi", "fastcgi interface to webserver"),
99  fSocket(0),
100  fDebugMode(kFALSE),
101  fTopName(),
102  fThrd(0)
103 {
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// destructor
108 
110 {
111  if (fThrd) {
112  // running thread will be killed
113  fThrd->Kill();
114  delete fThrd;
115  fThrd = 0;
116  }
117 
118  if (fSocket > 0) {
119  // close opened socket
120  close(fSocket);
121  fSocket = 0;
122  }
123 }
124 
125 ////////////////////////////////////////////////////////////////////////////////
126 /// initializes fastcgi variables and start thread,
127 /// which will process incoming http requests
128 
129 Bool_t TFastCgi::Create(const char *args)
130 {
131 #ifndef HTTP_WITHOUT_FASTCGI
132  FCGX_Init();
133 
134 // Info("Create", "Analyze url %s", s.Data());
135 
136  TString sport = ":9000";
137 
138  if ((args != 0) && (strlen(args) > 0)) {
139 
140  // first extract port number
141  sport = ":";
142  while ((*args != 0) && (*args >= '0') && (*args <= '9'))
143  sport.Append(*args++);
144 
145  // than search for extra parameters
146  while ((*args != 0) && (*args != '?')) args++;
147 
148  if (*args == '?') {
149  TUrl url(TString::Format("http://localhost/folder%s", args));
150 
151  if (url.IsValid()) {
152 
153  url.ParseOptions();
154 
155  if (url.GetValueFromOptions("debug") != 0) fDebugMode = kTRUE;
156 
157  const char *top = url.GetValueFromOptions("top");
158  if (top != 0) fTopName = top;
159  }
160  }
161 
162 // Info("Create", "valid url opt %s debug = %d", url.GetOptions(), fDebugMode);
163  }
164 
165  Info("Create", "Starting FastCGI server on port %s", sport.Data() + 1);
166 
167  fSocket = FCGX_OpenSocket(sport.Data(), 10);
168  fThrd = new TThread("FastCgiThrd", TFastCgi::run_func, this);
169  fThrd->Run();
170 
171  return kTRUE;
172 #else
173  (void)args;
174  Error("Create", "ROOT compiled without fastcgi support");
175  return kFALSE;
176 #endif
177 }
178 
179 
180 ////////////////////////////////////////////////////////////////////////////////
181 
182 void *TFastCgi::run_func(void *args)
183 {
184 #ifndef HTTP_WITHOUT_FASTCGI
185 
186  TFastCgi *engine = (TFastCgi *) args;
187 
188  FCGX_Request request;
189 
190  FCGX_InitRequest(&request, engine->GetSocket(), 0);
191 
192  int count = 0;
193 
194  while (1) {
195 
196  int rc = FCGX_Accept_r(&request);
197 
198  if (rc != 0) continue;
199 
200  count++;
201 
202  const char *inp_path = FCGX_GetParam("PATH_INFO", request.envp);
203  const char *inp_query = FCGX_GetParam("QUERY_STRING", request.envp);
204  const char *inp_method = FCGX_GetParam("REQUEST_METHOD", request.envp);
205  const char *inp_length = FCGX_GetParam("CONTENT_LENGTH", request.envp);
206 
207  THttpCallArg arg;
208  if (inp_path != 0) arg.SetPathAndFileName(inp_path);
209  if (inp_query != 0) arg.SetQuery(inp_query);
210  if (inp_method != 0) arg.SetMethod(inp_method);
211  if (engine->fTopName.Length() > 0) arg.SetTopName(engine->fTopName.Data());
212  int len = 0;
213  if (inp_length!=0) len = strtol(inp_length, NULL, 10);
214  if (len>0) {
215  void* buf = malloc(len+1); // one myte more for null-termination
216  int nread = FCGX_GetStr((char*) buf, len, request.in);
217  if (nread>0) arg.SetPostData(buf, nread);
218  else free(buf);
219  }
220 
221  TString header;
222  for (char **envp = request.envp; *envp != NULL; envp++) {
223  TString entry = *envp;
224  for (Int_t n=0;n<entry.Length();n++)
225  if (entry[n] == '=') { entry[n] = ':'; break; }
226  header.Append(entry);
227  header.Append("\r\n");
228  }
229  arg.SetRequestHeader(header);
230 
231  TString username = arg.GetRequestHeader("REMOTE_USER");
232  if ((username.Length()>0) && (arg.GetRequestHeader("AUTH_TYPE").Length()>0))
233  arg.SetUserName(username);
234 
235  if (engine->fDebugMode) {
236  FCGX_FPrintF(request.out,
237  "Status: 200 OK\r\n"
238  "Content-type: text/html\r\n"
239  "\r\n"
240  "<title>FastCGI echo</title>"
241  "<h1>FastCGI echo</h1>\n");
242 
243  FCGX_FPrintF(request.out, "Request %d:<br/>\n<pre>\n", count);
244  FCGX_FPrintF(request.out, " Method : %s\n", arg.GetMethod());
245  FCGX_FPrintF(request.out, " PathName : %s\n", arg.GetPathName());
246  FCGX_FPrintF(request.out, " FileName : %s\n", arg.GetFileName());
247  FCGX_FPrintF(request.out, " Query : %s\n", arg.GetQuery());
248  FCGX_FPrintF(request.out, " PostData : %ld\n", arg.GetPostDataLength());
249  FCGX_FPrintF(request.out, "</pre><p>\n");
250 
251  FCGX_FPrintF(request.out, "Environment:<br/>\n<pre>\n");
252  for (char **envp = request.envp; *envp != NULL; envp++) {
253  FCGX_FPrintF(request.out, " %s\n", *envp);
254  }
255  FCGX_FPrintF(request.out, "</pre><p>\n");
256 
257  FCGX_Finish_r(&request);
258  continue;
259  }
260 
261  TString fname;
262 
263  if (engine->GetServer()->IsFileRequested(inp_path, fname)) {
264  FCGX_ROOT_send_file(&request, fname.Data());
265  FCGX_Finish_r(&request);
266  continue;
267  }
268 
269 // printf("PATHNAME %s FILENAME %s QUERY %s \n",
270 // arg.GetPathName(), arg.GetFileName(), arg.GetQuery());
271 
272  TString hdr;
273 
274  if (!engine->GetServer()->ExecuteHttp(&arg) || arg.Is404()) {
275  arg.FillHttpHeader(hdr, "Status:");
276  FCGX_FPrintF(request.out, hdr.Data());
277  } else if (arg.IsFile()) {
278  FCGX_ROOT_send_file(&request, (const char *) arg.GetContent());
279  } else {
280 
281  // TODO: check in request header that gzip encoding is supported
282  if (arg.GetZipping() > 0) arg.CompressWithGzip();
283 
284  arg.FillHttpHeader(hdr, "Status:");
285  FCGX_FPrintF(request.out, hdr.Data());
286 
287  FCGX_PutStr((const char *) arg.GetContent(),
288  (int) arg.GetContentLength(), request.out);
289  }
290 
291  FCGX_Finish_r(&request);
292 
293  } /* while */
294 
295  return 0;
296 
297 #else
298  return args;
299 #endif
300 }
301 
THttpServer * GetServer() const
Definition: THttpEngine.h:41
virtual Bool_t Create(const char *args)
initializes fastcgi variables and start thread, which will process incoming http requests ...
Definition: TFastCgi.cxx:129
void SetRequestHeader(const char *h)
Definition: THttpCallArg.h:114
Ssiz_t Length() const
Definition: TString.h:390
TThread * fThrd
name of top item
Definition: TFastCgi.h:18
This class represents a WWW compatible URL.
Definition: TUrl.h:41
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
const char * GetPathName() const
Definition: THttpCallArg.h:177
Basic string class.
Definition: TString.h:137
void SetQuery(const char *q)
Definition: THttpCallArg.h:101
int Int_t
Definition: RtypesCore.h:41
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kFALSE
Definition: Rtypes.h:92
#define malloc
Definition: civetweb.c:818
const void * GetContent() const
Definition: THttpCallArg.h:332
void SetUserName(const char *n)
Definition: THttpCallArg.h:94
Int_t fSocket
Definition: TFastCgi.h:15
void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
Definition: TFastCgi.cxx:24
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
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...
TString fTopName
debug mode, may required for fastcgi debugging in other servers
Definition: TFastCgi.h:17
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.
Bool_t IsFile() const
Definition: THttpCallArg.h:315
TString & Append(const char *cs)
Definition: TString.h:492
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
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
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
Int_t Kill()
Kill this thread.
Definition: TThread.cxx:576
Bool_t ExecuteHttp(THttpCallArg *arg)
Execute HTTP request.
#define ClassImp(name)
Definition: Rtypes.h:279
void SetMethod(const char *method)
Definition: THttpCallArg.h:64
#define free
Definition: civetweb.c:821
Bool_t fDebugMode
socket used by fastcgi
Definition: TFastCgi.h:16
Int_t GetZipping() const
Definition: THttpCallArg.h:288
static const char * GetMimeType(const char *path)
Guess mime type base on file extension.
typedef void((*Func_t)())
static char * ReadFileContent(const char *filename, Int_t &len)
Reads content of file from the disk.
void ParseOptions() const
Parse URL options into a key/value map.
Definition: TUrl.cxx:618
Int_t GetSocket() const
Definition: TFastCgi.h:23
#define NULL
Definition: Rtypes.h:82
virtual ~TFastCgi()
destructor
Definition: TFastCgi.cxx:109
Long_t GetPostDataLength() const
Definition: THttpCallArg.h:170
const Bool_t kTRUE
Definition: Rtypes.h:91
static void * run_func(void *)
Definition: TFastCgi.cxx:182
TString GetRequestHeader(const char *name)
Definition: THttpCallArg.h:135
const Int_t n
Definition: legend1.C:16
Bool_t CompressWithGzip()
compress reply data with gzip compression
void FillHttpHeader(TString &buf, const char *header=0)
fill HTTP header