From b67d33c344ba1ad8be9c4bf3a15937d03c10f440 Mon Sep 17 00:00:00 2001 From: Sergey Linev <S.Linev@gsi.de> Date: Fri, 29 Jan 2021 14:27:11 +0100 Subject: [PATCH] [http] use std::thread to run FastCGI thread Prepare to multithreaded FastCGI implementation --- net/http/src/TFastCgi.cxx | 253 +++++++++++++++++++------------------- net/http/src/TFastCgi.h | 20 +-- 2 files changed, 141 insertions(+), 132 deletions(-) diff --git a/net/http/src/TFastCgi.cxx b/net/http/src/TFastCgi.cxx index 28f1bf4a5d3..cdac93b5420 100644 --- a/net/http/src/TFastCgi.cxx +++ b/net/http/src/TFastCgi.cxx @@ -53,6 +53,36 @@ void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname) #endif + +// simple run function to process all requests in same thread + +void run_single_thread(TFastCgi *engine) +{ +#ifndef HTTP_WITHOUT_FASTCGI + + FCGX_Request request; + + FCGX_InitRequest(&request, engine->GetSocket(), 0); + + while (!engine->IsTerminating()) { + + int rc = FCGX_Accept_r(&request); + + if (rc != 0) + continue; + + engine->ProcessRequest(&request); + + FCGX_Finish_r(&request); + } + +#else + (void) engine; +#endif +} + + + ////////////////////////////////////////////////////////////////////////// // // // TFastCgi // @@ -94,8 +124,7 @@ void FCGX_ROOT_send_file(FCGX_Request *request, const char *fname) /// normal constructor TFastCgi::TFastCgi() - : THttpEngine("fastcgi", "fastcgi interface to webserver"), fSocket(0), fDebugMode(kFALSE), fTopName(), - fThrd(nullptr), fTerminating(kFALSE) + : THttpEngine("fastcgi", "fastcgi interface to webserver") { } @@ -106,12 +135,9 @@ TFastCgi::~TFastCgi() { fTerminating = kTRUE; - if (fThrd) { - // running thread will be killed - fThrd->Kill(); - delete fThrd; - fThrd = nullptr; - } + // running thread will be killed + if (fThrd) + fThrd->join(); if (fSocket > 0) { // close opened socket @@ -162,12 +188,13 @@ Bool_t TFastCgi::Create(const char *args) Info("Create", "Starting FastCGI server on port %s", sport.Data() + 1); fSocket = FCGX_OpenSocket(sport.Data(), 10); - fThrd = new TThread("FastCgiThrd", TFastCgi::run_func, this); - fThrd->Run(); + if (!fSocket) return kFALSE; + + fThrd = std::make_unique<std::thread>(run_single_thread, this); return kTRUE; #else - (void)args; + (void) args; Error("Create", "ROOT compiled without fastcgi support"); return kFALSE; #endif @@ -193,129 +220,107 @@ public: Bool_t CanPostpone() const override { return kFALSE; } }; -//////////////////////////////////////////////////////////////////////////////// -void *TFastCgi::run_func(void *args) +void TFastCgi::ProcessRequest(void *req) { -#ifndef HTTP_WITHOUT_FASTCGI - - TFastCgi *engine = (TFastCgi *)args; - - FCGX_Request request; - - FCGX_InitRequest(&request, engine->GetSocket(), 0); - +#ifdef HTTP_WITHOUT_FASTCGI + (void) req; +#else int count = 0; + count++; // simple static request counter + + FCGX_Request *request = (FCGX_Request *) req; + + const char *inp_path = FCGX_GetParam("PATH_INFO", request->envp); + if (!inp_path) inp_path = FCGX_GetParam("SCRIPT_FILENAME", request->envp); + const char *inp_query = FCGX_GetParam("QUERY_STRING", request->envp); + const char *inp_method = FCGX_GetParam("REQUEST_METHOD", request->envp); + const char *inp_length = FCGX_GetParam("CONTENT_LENGTH", request->envp); + + auto arg = std::make_shared<TFastCgiCallArg>(); + if (inp_path) + arg->SetPathAndFileName(inp_path); + if (inp_query) + arg->SetQuery(inp_query); + if (inp_method) + arg->SetMethod(inp_method); + if (fTopName.Length() > 0) + arg->SetTopName(fTopName.Data()); + int len = 0; + if (inp_length) + len = strtol(inp_length, nullptr, 10); + if (len > 0) { + std::string buf; + buf.resize(len); + int nread = FCGX_GetStr((char *)buf.data(), len, request->in); + if (nread == len) + arg->SetPostData(std::move(buf)); + } - while (!engine->fTerminating) { - - int rc = FCGX_Accept_r(&request); - - if (rc != 0) - continue; - - count++; - - const char *inp_path = FCGX_GetParam("PATH_INFO", request.envp); - if (!inp_path) inp_path = FCGX_GetParam("SCRIPT_FILENAME", request.envp); - const char *inp_query = FCGX_GetParam("QUERY_STRING", request.envp); - const char *inp_method = FCGX_GetParam("REQUEST_METHOD", request.envp); - const char *inp_length = FCGX_GetParam("CONTENT_LENGTH", request.envp); - - auto arg = std::make_shared<TFastCgiCallArg>(); - if (inp_path) - arg->SetPathAndFileName(inp_path); - if (inp_query) - arg->SetQuery(inp_query); - if (inp_method) - arg->SetMethod(inp_method); - if (engine->fTopName.Length() > 0) - arg->SetTopName(engine->fTopName.Data()); - int len = 0; - if (inp_length) - len = strtol(inp_length, NULL, 10); - if (len > 0) { - std::string buf; - buf.resize(len); - int nread = FCGX_GetStr((char *)buf.data(), len, request.in); - if (nread == len) - arg->SetPostData(std::move(buf)); - } - - TString header; - for (char **envp = request.envp; *envp != NULL; envp++) { - TString entry = *envp; - for (Int_t n = 0; n < entry.Length(); n++) - if (entry[n] == '=') { - entry[n] = ':'; - break; - } - header.Append(entry); - header.Append("\r\n"); - } - arg->SetRequestHeader(header); - - TString username = arg->GetRequestHeader("REMOTE_USER"); - if ((username.Length() > 0) && (arg->GetRequestHeader("AUTH_TYPE").Length() > 0)) - arg->SetUserName(username); - - if (engine->fDebugMode) { - FCGX_FPrintF(request.out, "Status: 200 OK\r\n" - "Content-type: text/html\r\n" - "\r\n" - "<title>FastCGI echo</title>" - "<h1>FastCGI echo</h1>\n"); - - FCGX_FPrintF(request.out, "Request %d:<br/>\n<pre>\n", count); - FCGX_FPrintF(request.out, " Method : %s\n", arg->GetMethod()); - FCGX_FPrintF(request.out, " PathName : %s\n", arg->GetPathName()); - FCGX_FPrintF(request.out, " FileName : %s\n", arg->GetFileName()); - FCGX_FPrintF(request.out, " Query : %s\n", arg->GetQuery()); - FCGX_FPrintF(request.out, " PostData : %ld\n", arg->GetPostDataLength()); - FCGX_FPrintF(request.out, "</pre><p>\n"); - - FCGX_FPrintF(request.out, "Environment:<br/>\n<pre>\n"); - for (char **envp = request.envp; *envp != NULL; envp++) { - FCGX_FPrintF(request.out, " %s\n", *envp); + TString header; + for (char **envp = request->envp; *envp != nullptr; envp++) { + TString entry = *envp; + for (Int_t n = 0; n < entry.Length(); n++) + if (entry[n] == '=') { + entry[n] = ':'; + break; } - FCGX_FPrintF(request.out, "</pre><p>\n"); - - FCGX_Finish_r(&request); - continue; - } - - TString fname; - - if (engine->GetServer()->IsFileRequested(inp_path, fname)) { - FCGX_ROOT_send_file(&request, fname.Data()); - FCGX_Finish_r(&request); - continue; - } - - if (!engine->GetServer()->ExecuteHttp(arg) || arg->Is404()) { - std::string hdr = arg->FillHttpHeader("Status:"); - FCGX_FPrintF(request.out, hdr.c_str()); - } else if (arg->IsFile()) { - FCGX_ROOT_send_file(&request, (const char *)arg->GetContent()); - } else { - - // TODO: check in request header that gzip encoding is supported - if (arg->GetZipping() != THttpCallArg::kNoZip) - arg->CompressWithGzip(); + header.Append(entry); + header.Append("\r\n"); + } + arg->SetRequestHeader(header); + + TString username = arg->GetRequestHeader("REMOTE_USER"); + if ((username.Length() > 0) && (arg->GetRequestHeader("AUTH_TYPE").Length() > 0)) + arg->SetUserName(username); + + if (fDebugMode) { + FCGX_FPrintF(request->out, "Status: 200 OK\r\n" + "Content-type: text/html\r\n" + "\r\n" + "<title>FastCGI echo</title>" + "<h1>FastCGI echo</h1>\n"); + + FCGX_FPrintF(request->out, "Request %d:<br/>\n<pre>\n", count); + FCGX_FPrintF(request->out, " Method : %s\n", arg->GetMethod()); + FCGX_FPrintF(request->out, " PathName : %s\n", arg->GetPathName()); + FCGX_FPrintF(request->out, " FileName : %s\n", arg->GetFileName()); + FCGX_FPrintF(request->out, " Query : %s\n", arg->GetQuery()); + FCGX_FPrintF(request->out, " PostData : %ld\n", arg->GetPostDataLength()); + FCGX_FPrintF(request->out, "</pre><p>\n"); + + FCGX_FPrintF(request->out, "Environment:<br/>\n<pre>\n"); + for (char **envp = request->envp; *envp != nullptr; envp++) + FCGX_FPrintF(request->out, " %s\n", *envp); + FCGX_FPrintF(request->out, "</pre><p>\n"); + + return; + } - std::string hdr = arg->FillHttpHeader("Status:"); - FCGX_FPrintF(request.out, hdr.c_str()); + TString fname; - FCGX_PutStr((const char *)arg->GetContent(), (int)arg->GetContentLength(), request.out); - } + if (GetServer()->IsFileRequested(inp_path, fname)) { + FCGX_ROOT_send_file(request, fname.Data()); + return; + } - FCGX_Finish_r(&request); + if (!GetServer()->ExecuteHttp(arg) || arg->Is404()) { + std::string hdr = arg->FillHttpHeader("Status:"); + FCGX_FPrintF(request->out, hdr.c_str()); + } else if (arg->IsFile()) { + FCGX_ROOT_send_file(request, (const char *)arg->GetContent()); + } else { - } /* while */ + // TODO: check in request header that gzip encoding is supported + if (arg->GetZipping() != THttpCallArg::kNoZip) + arg->CompressWithGzip(); - return nullptr; + std::string hdr = arg->FillHttpHeader("Status:"); + FCGX_FPrintF(request->out, hdr.c_str()); -#else - return args; + FCGX_PutStr((const char *)arg->GetContent(), (int)arg->GetContentLength(), request->out); + } #endif + } + diff --git a/net/http/src/TFastCgi.h b/net/http/src/TFastCgi.h index 86f3da51895..f7c6eadcc74 100644 --- a/net/http/src/TFastCgi.h +++ b/net/http/src/TFastCgi.h @@ -14,15 +14,16 @@ #include "THttpEngine.h" -class TThread; +#include <thread> +#include <memory> class TFastCgi : public THttpEngine { protected: - Int_t fSocket; ///<! socket used by fastcgi - Bool_t fDebugMode; ///<! debug mode, may required for fastcgi debugging in other servers - TString fTopName; ///<! name of top item - TThread *fThrd; ///<! thread which takes requests, can be many later - Bool_t fTerminating; ///<! set when http server wants to terminate all engines + Int_t fSocket{0}; ///<! socket used by fastcgi + Bool_t fDebugMode{kFALSE}; ///<! debug mode, may required for fastcgi debugging in other servers + TString fTopName; ///<! name of top item + std::unique_ptr<std::thread> fThrd; ///<! thread which takes requests, can be many later + Bool_t fTerminating{kFALSE}; ///<! set when http server wants to terminate all engines virtual void Terminate() { fTerminating = kTRUE; } @@ -30,11 +31,14 @@ public: TFastCgi(); virtual ~TFastCgi(); + virtual Bool_t Create(const char *args); + Int_t GetSocket() const { return fSocket; } - virtual Bool_t Create(const char *args); + Bool_t IsTerminating() const { return fTerminating; } + + void ProcessRequest(void *req); - static void *run_func(void *); }; #endif -- GitLab