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:
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{
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
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
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 if (arg->GetZipping() != THttpCallArg::kNoZip)
164 arg->CompressWithGzip();
165
166 std::string hdr = arg->FillHttpHeader("Status:");
167 FCGX_FPrintF(request->out, hdr.c_str());
168
169 FCGX_PutStr((const char *)arg->GetContent(), (int)arg->GetContentLength(), request->out);
170 }
171}
172
174{
175 std::condition_variable cond; ///<! condition used to wait for processing
176 std::mutex m;
177 std::unique_ptr<FCGX_Request> arg;
178 int nwaiting = 0;
179
180 auto worker_func = [engine, &cond, &m, &arg, &nwaiting]() {
181
182 while (!engine->IsTerminating()) {
183
184 std::unique_ptr<FCGX_Request> request;
185
186 bool can_postpone = false;
187
188 {
189 std::unique_lock<std::mutex> lk(m);
190 nwaiting++;
191 cond.wait(lk);
192 nwaiting--;
193 can_postpone = (nwaiting > 5);
194 std::swap(arg, request);
195 }
196
197 if (request) {
198 process_request(engine, request.get(), can_postpone);
199
200 FCGX_Finish_r(request.get());
201 }
202 }
203
204 };
205
206 // start N workers
207 std::vector<std::thread> workers;
208 for (int n=0; n< nthrds; ++n)
209 workers.emplace_back(worker_func);
210
211 while (!engine->IsTerminating()) {
212 auto request = std::make_unique<FCGX_Request>();
213
214 FCGX_InitRequest(request.get(), engine->GetSocket(), 0);
215
216 int rc = FCGX_Accept_r(request.get());
217
218 if (rc != 0)
219 continue;
220
221 {
222 std::lock_guard<std::mutex> lk(m);
223 if (nwaiting > 0)
224 std::swap(request, arg);
225 }
226
227 if (!request) {
228 // notify thread to process request
229 cond.notify_one();
230 } else {
231 // process request ourselfs
232 process_request(engine, request.get(), false);
233 FCGX_Finish_r(request.get());
234 }
235 }
236
237 // ensure that all threads are waked up
238 cond.notify_all();
239
240 // join all workers
241 for (auto & thrd : workers)
242 thrd.join();
243
244}
245
246// simple run function to process all requests in same thread
247
249{
250
251 FCGX_Request request;
252
253 FCGX_InitRequest(&request, engine->GetSocket(), 0);
254
255 while (!engine->IsTerminating()) {
256
257 int rc = FCGX_Accept_r(&request);
258
259 if (rc != 0)
260 continue;
261
262 process_request(engine, &request, false);
263
264 FCGX_Finish_r(&request);
265 }
266
267}
268
269#endif
270
271
272/** \class TFastCgi
273
274THttpEngine implementation, based on fastcgi package
275
276Allows to redirect http requests from normal web server like
277Apache2 or lighttpd
278
279Configuration example for lighttpd:
280
281 server.modules += ( "mod_fastcgi" )
282 fastcgi.server = (
283 "/remote_scripts/" =>
284 (( "host" => "192.168.1.11",
285 "port" => 9000,
286 "check-local" => "disable",
287 "docroot" => "/"
288 ))
289 )
290
291When creating THttpServer, one should specify:
292
293 THttpServer* serv = new THttpServer("fastcgi:9000");
294
295In this case, requests to lighttpd server will be
296redirected to ROOT session. Like: `http://lighttpdhost/remote_scripts/root.cgi/`
297
298Following additional options can be specified
299
300 top=foldername - name of top folder, seen in the browser
301 thrds=N - run N worker threads to process requests, default 10
302 debug=1 - run fastcgi server in debug mode
303
304Example:
305
306 serv->CreateEngine("fastcgi:9000?top=fastcgiserver");
307*/
308
309////////////////////////////////////////////////////////////////////////////////
310/// normal constructor
311
313 : THttpEngine("fastcgi", "fastcgi interface to webserver")
314{
315}
316
317////////////////////////////////////////////////////////////////////////////////
318/// destructor
319
321{
323
324 // running thread will stopped
325 if (fThrd)
326 fThrd->join();
327
328 if (fSocket > 0) {
329 // close opened socket
330 close(fSocket);
331 fSocket = 0;
332 }
333}
334
335////////////////////////////////////////////////////////////////////////////////
336/// create engine data
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
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void process_request(TFastCgi *engine, FCGX_Request *request, bool can_postpone)
Definition TFastCgi.cxx:78
void run_single_thread(TFastCgi *engine)
Definition TFastCgi.cxx:248
void run_multi_threads(TFastCgi *engine, Int_t nthrds)
Definition TFastCgi.cxx:173
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:312
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:320
Bool_t Create(const char *args) override
create engine data
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
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:1096
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1070
Basic string class.
Definition TString.h:138
TString & Append(const char *cs)
Definition TString.h:583
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:2459
This class represents a WWW compatible URL.
Definition TUrl.h:33
const Int_t n
Definition legend1.C:16
TMarker m
Definition textangle.C:8