24#include <fastcgi++/log.hpp>
25#include <fastcgi++/manager.hpp>
26#include <fastcgi++/request.hpp>
39 Log logger(
"fastcgi");
44 enum class RawPostAccess {
50 class FastcgippRequestAdapter :
public Fastcgipp::Request<char> {
51 shared_ptr<string> rawPost;
58 FastcgippRequestAdapter() : Fastcgipp::
Request<char>() {}
64 bool response()
override;
71 bool inProcessor()
override;
75bool FastcgippRequestAdapter::response() {
77 auto requestHandler = any_cast<RequestHandler*>(m_externalObject);
81 auto const& renv = environment();
82 auto renvp = [&](
string const& k) {
83 return renv.parameters.count(k) ? renv.parameters.at(k) : string();
86 {
"content-type", renvp(
"CONTENT_TYPE")},
92 auto https = renv.parameters.count(
"HTTPS");
93 baseUrl << (https ?
"https://" :
"http://")
94 << renvp(
"HTTP_HOST");
95 auto baseUrlStr = baseUrl.str();
97 auto requestUri = renvp(
"REQUEST_URI");
100 requestInit.
environment[
"FULL_URL_WITH_QS"] = baseUrlStr + requestUri;
103 baseUrl << requestUri.substr(0, requestUri.find_first_of(
'?'));
104 requestInit.
environment[
"FULL_URL_WITHOUT_QS"] = baseUrl.str();
107 for (
auto const& [k, v] : renv.parameters) {
111 if (k.substr(0, 5) ==
"HTTP_") {
130 for (
auto const& [k, fcgiFile] : renv.files) {
131 requestInit.
postFiles.insert({k,
File(fcgiFile.data, fcgiFile.size).
filename(fcgiFile.filename).contentType(fcgiFile.contentType)});
136 connectionInit.
requestInit = std::move(requestInit);
137 connectionInit.
config = *requestHandler->getConfig();
141 if (!flushInfo.flushedBefore) {
142 response =
"status: " + flushInfo.getStatusString() +
"\r\n";
144 response += flushInfo.getFullHttp();
145 dump(response.c_str(), response.size());
149 requestHandler->handleRequest(connection);
150 connection.flushResponse();
155bool FastcgippRequestAdapter::inProcessor() {
156 auto requestHandler = any_cast<RequestHandler*>(m_externalObject);
157 auto postContentType = environment().contentType;
158 auto configPtr = requestHandler->getConfig();
160 string rawPostAccess = (*configPtr)[{
"post",
"raw_access"}];
161 if (postContentType.empty() || rawPostAccess ==
"never" ||
162 (rawPostAccess !=
"always" && (postContentType ==
"multipart/form-data" || postContentType ==
"application/x-www-form-urlencoded"))) {
166 auto postBuffer = environment().postBuffer();
167 rawPost = make_shared<string>(postBuffer.data(), postBuffer.size());
171struct FastcgiRequestHandler::Data {
172 unique_ptr<Fastcgipp::Manager<FastcgippRequestAdapter>> fastcgippManager;
173 bool requestHandlingActive =
false;
177FastcgiRequestHandler::FastcgiRequestHandler(std::shared_ptr<HandleRequestFunctionWrapper> handleRequestFunction,
178 Config config,
int concurrency) {
179 data = make_unique<Data>();
187 postMax = configPtr->isSet({
"post",
"max_size"})
188 ?
static_cast<size_t>(stoul((*configPtr)[{
"post",
"max_size"}])) * 1024
190 }
catch (invalid_argument& e) {
191 NLOG_WARNING(logger,
"WARNING: Invalid value given for post/max_size given in the config file.")
195 Fastcgipp::Logging::addHeader =
false;
196 Fastcgipp::Logging::logFunction = [&](
string const& msg, Fastcgipp::Logging::Level level) {
199 case Fastcgipp::Logging::INFO:
202 case Fastcgipp::Logging::FAIL:
203 case Fastcgipp::Logging::ERROR:
206 case Fastcgipp::Logging::WARNING:
209 case Fastcgipp::Logging::DEBUG:
210 case Fastcgipp::Logging::DIAG:
216 logger.write(msg, nawaLevel);
219 data->fastcgippManager = make_unique<Fastcgipp::Manager<FastcgippRequestAdapter>>(concurrency, postMax,
223 string mode = (*configPtr)[{
"fastcgi",
"mode"}];
225 auto fastcgiListen = (*configPtr)[{
"fastcgi",
"listen"}];
226 auto fastcgiPort = (*configPtr)[{
"fastcgi",
"port"}];
227 if (fastcgiListen.empty())
228 fastcgiListen =
"127.0.0.1";
229 char const* fastcgiListenC = fastcgiListen.c_str();
230 if (fastcgiListen ==
"all")
231 fastcgiListenC =
nullptr;
232 if (fastcgiPort.empty())
233 fastcgiPort =
"8000";
234 if (!data->fastcgippManager->listen(fastcgiListenC, fastcgiPort.c_str())) {
236 "Could not create TCP socket for FastCGI.");
238 }
else if (mode ==
"unix") {
239 uint32_t permissions = 0xffffffffUL;
240 string permStr = (*configPtr)[{
"fastcgi",
"permissions"}];
242 if (!permStr.empty()) {
243 char const* psptr = permStr.c_str();
245 long perm = strtol(psptr, &endptr, 8);
246 if (*endptr ==
'\0') {
247 permissions = (uint32_t) perm;
251 auto fastcgiSocketPath = (*configPtr)[{
"fastcgi",
"path"}];
252 if (fastcgiSocketPath.empty()) {
253 fastcgiSocketPath =
"/etc/nawarun/sock.d/nawarun.sock";
255 auto fastcgiOwner = (*configPtr)[{
"fastcgi",
"owner"}];
256 auto fastcgiGroup = (*configPtr)[{
"fastcgi",
"group"}];
258 if (!data->fastcgippManager->listen(fastcgiSocketPath.c_str(), permissions,
259 fastcgiOwner.empty() ?
nullptr : fastcgiOwner.c_str(),
260 fastcgiGroup.empty() ?
nullptr : fastcgiGroup.c_str())) {
262 "Could not create UNIX socket for FastCGI.");
266 "Unknown FastCGI socket mode in configuration.");
270 if ((*configPtr)[{
"fastcgi",
"reuseaddr"}] !=
"off") {
271 data->fastcgippManager->reuseAddress(
true);
276 if (data->requestHandlingActive && !data->joined) {
277 data->fastcgippManager->terminate();
280 data->fastcgippManager->join();
281 data->fastcgippManager.reset(
nullptr);
286 if (data->requestHandlingActive) {
290 throw Exception(__PRETTY_FUNCTION__, 10,
"FastcgiRequestHandler was already joined.");
292 if (data->fastcgippManager) {
294 data->fastcgippManager->start();
295 data->requestHandlingActive =
true;
298 "An unknown error occurred during start of request handling.");
301 throw Exception(__PRETTY_FUNCTION__, 2,
"FastCGI manager is not available.");
309 if (data->fastcgippManager) {
310 data->fastcgippManager->stop();
318 if (data->fastcgippManager) {
319 data->fastcgippManager->terminate();
327 if (data->fastcgippManager) {
328 data->fastcgippManager->join();
330 data->fastcgippManager.reset(
nullptr);
Container used by request handlers to initiate the nawa::Connection object.
Response object to be passed back to NAWA and accessor to the request.
Exception class that can be used by apps to catch errors resulting from nawa function calls.
Class which connects NAWA to the fastcgi++ library.
Simple class for (not (yet) thread-safe) logging to stderr or to any other output stream.
#define NLOG_WARNING(Logger, Message)
Handles and serves incoming requests via the NAWA app.
~FastcgiRequestHandler() override
void stop() noexcept override
void join() noexcept override
void terminate() noexcept override
std::string & filename() noexcept
std::shared_ptr< Config const > getConfig() const noexcept
void setConfig(Config config) noexcept
void setAppRequestHandler(std::shared_ptr< HandleRequestFunctionWrapper > handleRequestFunction) noexcept
std::string toLowercase(std::string s)
std::unordered_multimap< KeyType, ValueType > toUnorderedMultimap(MapType< KeyType, ValueType, Args... > inputMap)
std::string stringReplace(std::string input, std::unordered_map< char, char > const &patterns)
std::unordered_multimap< std::string, std::string > getVars
std::string postContentType
std::unordered_map< std::string, std::string > environment
std::unordered_multimap< std::string, std::string > cookieVars
std::unordered_multimap< std::string, File > postFiles
RequestInitContainer requestInit
std::unordered_multimap< std::string, std::string > postVars
FlushCallbackFunction flushCallback
std::shared_ptr< std::string > rawPost
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.