// SPDX-FileCopyrightText: 2023 Yury Gubich // SPDX-License-Identifier: GPL-3.0-or-later #include "request.h" #include "response/response.h" constexpr static const char* GET("GET"); constexpr static const char* REQUEST_METHOD("REQUEST_METHOD"); constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME"); constexpr static const char* SERVER_NAME("SERVER_NAME"); // constexpr static const char* REQUEST_URI("REQUEST_URI"); // // constexpr static const char* DOCUMENT_URI("DOCUMENT_URI"); // constexpr static const char* DOCUMENT_ROOT("DOCUMENT_ROOT"); // // constexpr static const char* SCRIPT_NAME("SCRIPT_NAME"); constexpr std::array< std::pair, static_cast(Request::Method::unknown) > methods = {{ {"GET", Request::Method::get}, {"POST", Request::Method::post} }}; Request::Request (): state(State::initial), raw(), response(nullptr) {} Request::~Request() { terminate(); } void Request::terminate() { switch (state) { case State::accepted: case State::responded: FCGX_Finish_r(&raw); break; default: break; } } Request::Method Request::method() const { if (state == State::initial) throw std::runtime_error("An attempt to read request method on not accepted request"); std::string_view method(FCGX_GetParam(REQUEST_METHOD, raw.envp)); for (const auto& pair : methods) { if (pair.first == method) return pair.second; } return Request::Method::unknown; } bool Request::wait(int socketDescriptor) { if (state != State::initial) throw std::runtime_error("An attempt to wait for new incomming request on a wrong request state"); FCGX_Request* request = &raw; FCGX_InitRequest(request, socketDescriptor, 0); int rc = FCGX_Accept_r(request); bool result = rc == 0; if (result) state = State::accepted; return result; } OStream Request::getOutputStream(const Response* response) { validateResponse(response); return OStream(raw.out); } OStream Request::getErrorStream(const Response* response) { validateResponse(response); return OStream(raw.err); } void Request::responseIsComplete(const Response* response) { switch (state) { case State::initial: throw std::runtime_error("An attempt to mark the request as complete, but it wasn't even accepted yet"); break; case State::accepted: throw std::runtime_error("An attempt to mark the request as complete, but it wasn't responded"); break; case State::responding: if (Request::response != response) throw std::runtime_error("An attempt to mark the request as complete by the different response who actually started responding"); Request::response = nullptr; state = State::responded; break; case State::responded: throw std::runtime_error("An attempt to mark the request as a complete for the second time"); break; } } void Request::validateResponse(const Response* response) { switch (state) { case State::initial: throw std::runtime_error("An attempt to request stream while the request wasn't even accepted yet"); break; case State::accepted: Request::response = response; state = State::responding; break; case State::responding: if (Request::response != response) throw std::runtime_error("Error handling a request: first time one response started replying, then another continued"); break; case State::responded: throw std::runtime_error("An attempt to request stream on a request that was already done responding"); break; } } Request::State Request::currentState() const { return state; } std::string Request::getPath(const std::string& serverName) const { if (state != State::accepted) throw std::runtime_error("An attempt to request path on a wrong request state"); std::string path; std::string_view scriptFileName(FCGX_GetParam(SCRIPT_FILENAME, raw.envp)); std::string::size_type snLocation = scriptFileName.find(serverName); if (snLocation != std::string::npos) { if (snLocation + serverName.size() < scriptFileName.size()) path = scriptFileName.substr(snLocation + serverName.size() + 1); } if (!path.empty()) { while (path.back() == '/') path.erase(path.end() - 1); } return path; } std::string Request::getServerName() const { if (state != State::accepted) throw std::runtime_error("An attempt to request server name on a wrong request state"); return FCGX_GetParam(SERVER_NAME, raw.envp);; } void Request::printEnvironment(std::ostream& out) { char **envp = raw.envp; for (int i = 0; envp[i] != nullptr; ++i) { out << envp[i] << "\n"; } } void Request::printEnvironment(nlohmann::json& out) { if (!out.is_object()) return; char **envp = raw.envp; for (int i = 0; envp[i] != nullptr; ++i) { std::string_view value(envp[i]); std::string::size_type pos = value.find('='); if (pos != std::string::npos) out[std::string(value.substr(0, pos))] = std::string(value.substr(pos + 1, value.size())); } }