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