Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TFastCgi.cxx
Go to the documentation of this file.
1// $Id$
2// Author: Sergey Linev 28/12/2013
3
4/*************************************************************************
5 * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TFastCgi.h"
13
14#include "TThread.h"
15#include "TUrl.h"
16#include "THttpServer.h"
17
18#include "ROOT/RMakeUnique.hxx"
19
20#include <cstring>
21
22#ifdef WIN32
23#include <io.h>
24#else
25#include <unistd.h>
26#endif
27
28////////////////////////////////////////////////////////////////////////////////
29
30class TFastCgiCallArg : public THttpCallArg {
31
32 bool fCanPostpone{false};
33
34protected:
35
36 void CheckWSPageContent(THttpWSHandler *) override
37 {
38 std::string search = "JSROOT.connectWebWindow({";
39 std::string replace = search + "socket_kind:\"longpoll\",";
40 ReplaceAllinContent(search, replace, true);
41 }
42
43public:
44 TFastCgiCallArg(bool can_postpone) : THttpCallArg(), fCanPostpone(can_postpone) {};
45
46 /** All FastCGI requests should be immediately replied to get slot for next */
47 Bool_t CanPostpone() const override { return fCanPostpone; }
48};
49
50////////////////////////////////////////////////////////////////////////////////
51
52
53#ifndef HTTP_WITHOUT_FASTCGI
54
55#include "fcgiapp.h"
56
57#include <stdlib.h>
58
59void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
60{
61 std::string buf = THttpServer::ReadFileContent(fname);
62
63 if (buf.empty()) {
64 FCGX_FPrintF(request->out,
65 "Status: 404 Not Found\r\n"
66 "Content-Length: 0\r\n" // Always set Content-Length
67 "Connection: close\r\n\r\n");
68 } else {
69
70 FCGX_FPrintF(request->out,
71 "Status: 200 OK\r\n"
72 "Content-Type: %s\r\n"
73 "Content-Length: %d\r\n" // Always set Content-Length
74 "\r\n",
75 THttpServer::GetMimeType(fname), (int) buf.length());
76
77 FCGX_PutStr(buf.c_str(), buf.length(), request->out);
78 }
79}
80
81
82void process_request(TFastCgi *engine, FCGX_Request *request, bool can_postpone)
83{
84 int count = 0;
85 count++; // simple static request counter
86
87 const char *inp_path = FCGX_GetParam("PATH_INFO", request->envp);
88 if (!inp_path) inp_path = FCGX_GetParam("SCRIPT_FILENAME", request->envp);
89 const char *inp_query = FCGX_GetParam("QUERY_STRING", request->envp);
90 const char *inp_method = FCGX_GetParam("REQUEST_METHOD", request->envp);
91 const char *inp_length = FCGX_GetParam("CONTENT_LENGTH", request->envp);
92
93 auto arg = std::make_shared<TFastCgiCallArg>(can_postpone);
94 if (inp_path)
95 arg->SetPathAndFileName(inp_path);
96 if (inp_query)
97 arg->SetQuery(inp_query);
98 if (inp_method)
99 arg->SetMethod(inp_method);
100 if (engine->GetTopName())
101 arg->SetTopName(engine->GetTopName());
102 int len = 0;
103 if (inp_length)
104 len = strtol(inp_length, nullptr, 10);
105 if (len > 0) {
106 std::string buf;
107 buf.resize(len);
108 int nread = FCGX_GetStr((char *)buf.data(), len, request->in);
109 if (nread == len)
110 arg->SetPostData(std::move(buf));
111 }
112
113 TString header;
114 for (char **envp = request->envp; *envp != nullptr; envp++) {
115 TString entry = *envp;
116 for (Int_t n = 0; n < entry.Length(); n++)
117 if (entry[n] == '=') {
118 entry[n] = ':';
119 break;
120 }
121 header.Append(entry);
122 header.Append("\r\n");
123 }
124 arg->SetRequestHeader(header);
125
126 TString username = arg->GetRequestHeader("REMOTE_USER");
127 if ((username.Length() > 0) && (arg->GetRequestHeader("AUTH_TYPE").Length() > 0))
128 arg->SetUserName(username);
129
130 if (engine->IsDebugMode()) {
131 FCGX_FPrintF(request->out, "Status: 200 OK\r\n"
132 "Content-type: text/html\r\n"
133 "\r\n"
134 "<title>FastCGI echo</title>"
135 "<h1>FastCGI echo</h1>\n");
136
137 FCGX_FPrintF(request->out, "Request %d:<br/>\n<pre>\n", count);
138 FCGX_FPrintF(request->out, " Method : %s\n", arg->GetMethod());
139 FCGX_FPrintF(request->out, " PathName : %s\n", arg->GetPathName());
140 FCGX_FPrintF(request->out, " FileName : %s\n", arg->GetFileName());
141 FCGX_FPrintF(request->out, " Query : %s\n", arg->GetQuery());
142 FCGX_FPrintF(request->out, " PostData : %ld\n", arg->GetPostDataLength());
143 FCGX_FPrintF(request->out, "</pre><p>\n");
144
145 FCGX_FPrintF(request->out, "Environment:<br/>\n<pre>\n");
146 for (char **envp = request->envp; *envp != nullptr; envp++)
147 FCGX_FPrintF(request->out, " %s\n", *envp);
148 FCGX_FPrintF(request->out, "</pre><p>\n");
149
150 return;
151 }
152
153 TString fname;
154
155 if (engine->GetServer()->IsFileRequested(inp_path, fname)) {
156 FCGX_ROOT_send_file(request, fname.Data());
157 return;
158 }
159
160 if (!engine->GetServer()->ExecuteHttp(arg) || arg->Is404()) {
161 std::string hdr = arg->FillHttpHeader("Status:");
162 FCGX_FPrintF(request->out, hdr.c_str());
163 } else if (arg->IsFile()) {
164 FCGX_ROOT_send_file(request, (const char *)arg->GetContent());
165 } else {
166
167 // TODO: check in request header that gzip encoding is supported
168 if (arg->GetZipping() != THttpCallArg::kNoZip)
169 arg->CompressWithGzip();
170
171 std::string hdr = arg->FillHttpHeader("Status:");
172 FCGX_FPrintF(request->out, hdr.c_str());
173
174 FCGX_PutStr((const char *)arg->GetContent(), (int)arg->GetContentLength(), request->out);
175 }
176}
177
178void run_multi_threads(TFastCgi *engine, Int_t nthrds)
179{
180 std::condition_variable cond; ///<! condition used to wait for processing
181 std::mutex m;
182 std::unique_ptr<FCGX_Request> arg;
183 int nwaiting = 0;
184
185 auto worker_func = [engine, &cond, &m, &arg, &nwaiting]() {
186
187 while (!engine->IsTerminating()) {
188
189 std::unique_ptr<FCGX_Request> request;
190
191 bool can_postpone = false;
192
193 {
194 std::unique_lock<std::mutex> lk(m);
195 nwaiting++;
196 cond.wait(lk);
197 nwaiting--;
198 can_postpone = (nwaiting > 5);
199 std::swap(arg, request);
200 }
201
202 if (request) {
203 process_request(engine, request.get(), can_postpone);
204
205 FCGX_Finish_r(request.get());
206 }
207 }
208
209 };
210
211 // start N workers
212 std::vector<std::thread> workers;
213 for (int n=0; n< nthrds; ++n)
214 workers.emplace_back(worker_func);
215
216 while (!engine->IsTerminating()) {
217 auto request = std::make_unique<FCGX_Request>();
218
219 FCGX_InitRequest(request.get(), engine->GetSocket(), 0);
220
221 int rc = FCGX_Accept_r(request.get());
222
223 if (rc != 0)
224 continue;
225
226 {
227 std::lock_guard<std::mutex> lk(m);
228 if (nwaiting > 0)
229 std::swap(request, arg);
230 }
231
232 if (!request) {
233 // notify thread to process request
234 cond.notify_one();
235 } else {
236 // process request ourselfs
237 process_request(engine, request.get(), false);
238 FCGX_Finish_r(request.get());
239 }
240 }
241
242 // ensure that all threads are waked up
243 cond.notify_all();
244
245 // join all workers
246 for (auto & thrd : workers)
247 thrd.join();
248
249}
250
251// simple run function to process all requests in same thread
252
254{
255
256 FCGX_Request request;
257
258 FCGX_InitRequest(&request, engine->GetSocket(), 0);
259
260 while (!engine->IsTerminating()) {
261
262 int rc = FCGX_Accept_r(&request);
263
264 if (rc != 0)
265 continue;
266
267 process_request(engine, &request, false);
268
269 FCGX_Finish_r(&request);
270 }
271
272}
273
274#endif
275
276
277//////////////////////////////////////////////////////////////////////////
278// //
279// TFastCgi //
280// //
281// http engine implementation, based on fastcgi package //
282// Allows to redirect http requests from normal web server like //
283// Apache or lighttpd //
284// //
285// Configuration example for lighttpd //
286// //
287// server.modules += ( "mod_fastcgi" ) //
288// fastcgi.server = ( //
289// "/remote_scripts/" => //
290// (( "host" => "192.168.1.11", //
291// "port" => 9000, //
292// "check-local" => "disable", //
293// "docroot" => "/" //
294// )) //
295// ) //
296// //
297// When creating THttpServer, one should specify: //
298// //
299// THttpServer* serv = new THttpServer("fastcgi:9000"); //
300// //
301// In this case, requests to lighttpd server will be //
302// redirected to ROOT session. Like: //
303// http://lighttpdhost/remote_scripts/root.cgi/ //
304// //
305// Following additional options can be specified //
306// top=foldername - name of top folder, seen in the browser //
307// thrds=N - run N worker threads to process requests, default 10 //
308// debug=1 - run fastcgi server in debug mode //
309// Example: //
310// serv->CreateEngine("fastcgi:9000?top=fastcgiserver"); //
311// //
312// //
313//////////////////////////////////////////////////////////////////////////
314
315////////////////////////////////////////////////////////////////////////////////
316/// normal constructor
317
319 : THttpEngine("fastcgi", "fastcgi interface to webserver")
320{
321}
322
323////////////////////////////////////////////////////////////////////////////////
324/// destructor
325
327{
329
330 // running thread will stopped
331 if (fThrd)
332 fThrd->join();
333
334 if (fSocket > 0) {
335 // close opened socket
336 close(fSocket);
337 fSocket = 0;
338 }
339}
340
341////////////////////////////////////////////////////////////////////////////////
342/// initializes fastcgi variables and start thread,
343/// which will process incoming http requests
344
345Bool_t TFastCgi::Create(const char *args)
346{
347#ifndef HTTP_WITHOUT_FASTCGI
348 FCGX_Init();
349
350 TString sport = ":9000";
351 Int_t nthrds = 10;
352
353 if ((args != 0) && (strlen(args) > 0)) {
354
355 // first extract port number
356 sport = ":";
357 while ((*args != 0) && (*args >= '0') && (*args <= '9'))
358 sport.Append(*args++);
359
360 // than search for extra parameters
361 while ((*args != 0) && (*args != '?'))
362 args++;
363
364 if (*args == '?') {
365 TUrl url(TString::Format("http://localhost/folder%s", args));
366
367 if (url.IsValid()) {
368
369 url.ParseOptions();
370
371 if (url.GetValueFromOptions("debug") != 0)
373
374 if (url.HasOption("thrds"))
375 nthrds = url.GetIntValueFromOptions("thrds");
376
377 const char *top = url.GetValueFromOptions("top");
378 if (top != 0)
379 fTopName = top;
380 }
381 }
382 }
383
384 Info("Create", "Starting FastCGI server on port %s", sport.Data() + 1);
385
386 fSocket = FCGX_OpenSocket(sport.Data(), 10);
387 if (!fSocket) return kFALSE;
388
389 if (nthrds > 0)
390 fThrd = std::make_unique<std::thread>(run_multi_threads, this, nthrds);
391 else
392 fThrd = std::make_unique<std::thread>(run_single_thread, this);
393
394 return kTRUE;
395#else
396 (void) args;
397 Error("Create", "ROOT compiled without fastcgi support");
398 return kFALSE;
399#endif
400}
int Int_t
Definition RtypesCore.h:45
const Bool_t kFALSE
Definition RtypesCore.h:92
bool Bool_t
Definition RtypesCore.h:63
const Bool_t kTRUE
Definition RtypesCore.h:91
void process_request(TFastCgi *engine, FCGX_Request *request, bool can_postpone)
Definition TFastCgi.cxx:82
void run_single_thread(TFastCgi *engine)
Definition TFastCgi.cxx:253
void run_multi_threads(TFastCgi *engine, Int_t nthrds)
Definition TFastCgi.cxx:178
void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname)
Definition TFastCgi.cxx:59
typedef void((*Func_t)())
TFastCgi()
normal constructor
Definition TFastCgi.cxx:318
Bool_t IsTerminating() const
Definition TFastCgi.h:38
TString fTopName
! name of top item
Definition TFastCgi.h:24
std::unique_ptr< std::thread > fThrd
! thread which takes requests, can be many later
Definition TFastCgi.h:25
Bool_t fDebugMode
! debug mode, may required for fastcgi debugging in other servers
Definition TFastCgi.h:23
const char * GetTopName() const
Definition TFastCgi.h:42
Int_t GetSocket() const
Definition TFastCgi.h:36
virtual ~TFastCgi()
destructor
Definition TFastCgi.cxx:326
Bool_t Create(const char *args) override
initializes fastcgi variables and start thread, which will process incoming http requests
Definition TFastCgi.cxx:345
Int_t fSocket
! socket used by fastcgi
Definition TFastCgi.h:22
Bool_t IsDebugMode() const
Definition TFastCgi.h:40
Bool_t fTerminating
! set when http server wants to terminate all engines
Definition TFastCgi.h:26
virtual Bool_t CanPostpone() const
Return true if reply can be postponed by server
void ReplaceAllinContent(const std::string &from, const std::string &to, bool once=false)
Replace all occurrences of.
virtual void CheckWSPageContent(THttpWSHandler *)
Method used to modify content of web page used by web socket handler.
THttpServer * GetServer() const
Returns pointer to THttpServer associated with engine.
Definition THttpEngine.h:40
Bool_t IsFileRequested(const char *uri, TString &res) const
Check if file is requested, thread safe.
Bool_t ExecuteHttp(std::shared_ptr< THttpCallArg > arg)
Execute HTTP request.
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.
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:893
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:867
Basic string class.
Definition TString.h:136
Ssiz_t Length() const
Definition TString.h:410
const char * Data() const
Definition TString.h:369
TString & Append(const char *cs)
Definition TString.h:564
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:2331
This class represents a WWW compatible URL.
Definition TUrl.h:33
const char * GetValueFromOptions(const char *key) const
Return a value for a given key from the URL options.
Definition TUrl.cxx:659
Bool_t IsValid() const
Definition TUrl.h:79
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:671
void ParseOptions() const
Parse URL options into a key/value map.
Definition TUrl.cxx:625
Bool_t HasOption(const char *key) const
Returns true if the given key appears in the URL options list.
Definition TUrl.cxx:682
const Int_t n
Definition legend1.C:16
auto * m
Definition textangle.C:8