Logo ROOT  
Reference Guide
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//////////////////////////////////////////////////////////////////////////
274// //
275// TFastCgi //
276// //
277// http engine implementation, based on fastcgi package //
278// Allows to redirect http requests from normal web server like //
279// Apache or lighttpd //
280// //
281// Configuration 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// //
293// When creating THttpServer, one should specify: //
294// //
295// THttpServer* serv = new THttpServer("fastcgi:9000"); //
296// //
297// In this case, requests to lighttpd server will be //
298// redirected to ROOT session. Like: //
299// http://lighttpdhost/remote_scripts/root.cgi/ //
300// //
301// Following additional options can be specified //
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// Example: //
306// serv->CreateEngine("fastcgi:9000?top=fastcgiserver"); //
307// //
308// //
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/// initializes fastcgi variables and start thread,
339/// which will process incoming http requests
340
341Bool_t TFastCgi::Create(const char *args)
342{
343#ifndef HTTP_WITHOUT_FASTCGI
344 FCGX_Init();
345
346 TString sport = ":9000";
347 Int_t nthrds = 10;
348
349 if ((args != 0) && (strlen(args) > 0)) {
350
351 // first extract port number
352 sport = ":";
353 while ((*args != 0) && (*args >= '0') && (*args <= '9'))
354 sport.Append(*args++);
355
356 // than search for extra parameters
357 while ((*args != 0) && (*args != '?'))
358 args++;
359
360 if (*args == '?') {
361 TUrl url(TString::Format("http://localhost/folder%s", args));
362
363 if (url.IsValid()) {
364
365 url.ParseOptions();
366
367 if (url.GetValueFromOptions("debug") != 0)
369
370 if (url.HasOption("thrds"))
371 nthrds = url.GetIntValueFromOptions("thrds");
372
373 const char *top = url.GetValueFromOptions("top");
374 if (top != 0)
375 fTopName = top;
376 }
377 }
378 }
379
380 Info("Create", "Starting FastCGI server on port %s", sport.Data() + 1);
381
382 fSocket = FCGX_OpenSocket(sport.Data(), 10);
383 if (!fSocket) return kFALSE;
384
385 if (nthrds > 0)
386 fThrd = std::make_unique<std::thread>(run_multi_threads, this, nthrds);
387 else
388 fThrd = std::make_unique<std::thread>(run_single_thread, this);
389
390 return kTRUE;
391#else
392 (void) args;
393 Error("Create", "ROOT compiled without fastcgi support");
394 return kFALSE;
395#endif
396}
int Int_t
Definition: RtypesCore.h:45
const Bool_t kFALSE
Definition: RtypesCore.h:101
const Bool_t kTRUE
Definition: RtypesCore.h:100
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
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
initializes fastcgi variables and start thread, which will process incoming http requests
Definition: TFastCgi.cxx:341
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
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:2336
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
void swap(RDirectoryEntry &e1, RDirectoryEntry &e2) noexcept
void(off) SmallVectorTemplateBase< T
auto * m
Definition: textangle.C:8