1
0
forked from blue/pica

first couple of requests

This commit is contained in:
Blue 2023-11-23 16:57:32 -03:00
parent 68e795f0e6
commit aae7873d67
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
11 changed files with 152 additions and 34 deletions

View File

@ -9,6 +9,7 @@ cmake_policy(SET CMP0076 NEW)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(nlohmann_json REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(FCGI fcgi)
@ -20,9 +21,10 @@ add_subdirectory(request)
add_subdirectory(response)
add_subdirectory(stream)
target_link_libraries(pica
target_link_libraries(pica PRIVATE
fcgi
fcgi++
nlohmann_json::nlohmann_json
)
install(TARGETS pica RUNTIME DESTINATION bin)

View File

@ -15,7 +15,7 @@ int main() {
return 1;
}
if (chmod(socketPath, 0770) != 0) {
if (chmod(socketPath, 0777) != 0) {
std::cerr << "Couldn't set socket permissions" << std::endl;
return 2;
}

View File

@ -63,16 +63,23 @@ OStream Request::getErrorStream() {
return OStream(raw.err);
}
std::string_view Request::getPath(const std::string& serverName) const {
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_view path;
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;
@ -88,6 +95,19 @@ std::string Request::getServerName() const {
void Request::printEnvironment(std::ostream& out) {
char **envp = raw.envp;
for (int i = 0; envp[i] != nullptr; ++i) {
out << envp[i] << "</br>";
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()));
}
}

View File

@ -6,6 +6,8 @@
#include <fcgiapp.h>
#include <nlohmann/json.hpp>
#include "stream/ostream.h"
class Request {
@ -30,9 +32,10 @@ public:
OStream getOutputStream();
OStream getErrorStream();
std::string_view getPath(const std::string& serverName) const;
std::string getPath(const std::string& serverName) const;
std::string getServerName() const;
void printEnvironment(std::ostream& out);
void printEnvironment(nlohmann::json& out);
private:
State state;

View File

@ -8,7 +8,8 @@ constexpr std::array<std::string_view, static_cast<uint8_t>(Response::Status::__
};
constexpr std::array<std::string_view, static_cast<uint8_t>(Response::ContentType::__size)> contentTypes = {
"Content-type: text/html"
"Content-type: text/plain",
"Content-type: application/json"
};
Response::Response():
@ -38,5 +39,11 @@ void Response::replyTo(Request& request) const {
}
void Response::setBody(const std::string& body) {
type = ContentType::text;
Response::body = body;
}
void Response::setBody(const nlohmann::json& body) {
type = ContentType::json;
Response::body = body.dump();
}

View File

@ -4,6 +4,8 @@
#include <array>
#include <string_view>
#include <nlohmann/json.hpp>
#include "request/request.h"
#include "stream/ostream.h"
@ -19,6 +21,7 @@ public:
enum class ContentType {
text,
json,
__size
};
Response();
@ -26,6 +29,7 @@ public:
void replyTo(Request& request) const;
void setBody(const std::string& body);
void setBody(const nlohmann::json& body);
private:
Status status;

View File

@ -1,9 +1,11 @@
set(HEADERS
server.h
router.h
)
set(SOURCES
server.cpp
router.cpp
)
target_sources(pica PRIVATE ${SOURCES})

41
server/router.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "router.h"
Router::Router():
table()
{
}
void Router::addRoute(const std::string& path, const Handler& handler) {
auto result = table.emplace(path, handler);
if (!result.second)
std::cerr << "could'not add route " + path + " to the routing table";
}
void Router::route(const std::string& path, std::unique_ptr<Request> request) {
auto itr = table.find(path);
if (itr == table.end())
return handleNotFound(path, std::move(request));
try {
bool result = itr->second(request.get());
if (!result)
handleInternalError(std::runtime_error("handler failed to handle the request"), std::move(request));
} catch (const std::exception& e) {
handleInternalError(e, std::move(request));
}
}
void Router::handleNotFound(const std::string& path, std::unique_ptr<Request> request) {
Response notFound(Response::Status::notFound);
notFound.setBody(std::string("Path \"") + path + "\" was not found");
notFound.replyTo(*request.get());
std::cerr << "Not found: " << path << std::endl;
}
void Router::handleInternalError(const std::exception& exception, std::unique_ptr<Request> request) {
Response error(Response::Status::internalError);
error.setBody(std::string(exception.what()));
error.replyTo(*request.get());
std::cerr << "Internal error: " << exception.what() << std::endl;
}

27
server/router.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <map>
#include <functional>
#include <string>
#include <memory>
#include "request/request.h"
#include "response/response.h"
class Router {
public:
using Handler = std::function<bool(Request*)>;
Router();
void addRoute(const std::string& path, const Handler& handler);
void route(const std::string& path, std::unique_ptr<Request> request);
private:
void handleNotFound(const std::string& path, std::unique_ptr<Request> request);
void handleInternalError(const std::exception& exception, std::unique_ptr<Request> request);
private:
std::map<std::string, Handler> table;
};

View File

@ -8,14 +8,15 @@ constexpr static const char* DOCUMENT_ROOT("DOCUMENT_ROOT");
constexpr static const char* SCRIPT_NAME("SCRIPT_NAME");
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
constexpr std::string_view info("info");
constexpr std::string_view env("env");
Server::Server():
terminating(false),
requestCount(0),
serverName(std::nullopt)
{}
serverName(std::nullopt),
router()
{
router.addRoute("info", Server::info);
router.addRoute("env", Server::printEnvironment);
}
Server::~Server() {}
@ -52,27 +53,34 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
}
try {
std::string_view sPath = request->getPath(serverName.value());
if (sPath == info) {
Response res;
res.setBody("Pica<br>\nVersion: 1.0.0<br>\nRequestsHandled: " + std::to_string(requestCount));
res.replyTo(*request.get());
} else if (sPath == env) {
std::ostringstream ss;
request->printEnvironment(ss);
Response res;
res.setBody(ss.str());
res.replyTo(*request.get());
} else {
Response notFound(Response::Status::notFound);
notFound.setBody(std::string("Path ") + sPath.data() + " was not found");
notFound.replyTo(*request.get());
std::cerr << "Not found: " << sPath.data() << std::endl;
}
std::string path = request->getPath(serverName.value());
router.route(path.data(), std::move(request));
} catch (const std::exception e) {
Response error(Response::Status::internalError);
error.setBody(e.what());
error.setBody(std::string(e.what()));
error.replyTo(*request.get());
}
}
bool Server::printEnvironment(Request* request) {
nlohmann::json body = nlohmann::json::object();
request->printEnvironment(body);
Response res;
res.setBody(body);
res.replyTo(*request);
return true;
}
bool Server::info(Request* request) {
Response res;
nlohmann::json body = nlohmann::json::object();
body["type"] = "Pica";
body["version"] = "0.0.1";
res.setBody(body);
res.replyTo(*request);
return true;
}

View File

@ -12,8 +12,11 @@
#include <fcgio.h>
#include <stdint.h>
#include <nlohmann/json.hpp>
#include "request/request.h"
#include "response/response.h"
#include "router.h"
class Server {
public:
@ -24,12 +27,13 @@ public:
private:
void handleRequest(std::unique_ptr<Request> request);
void router(const std::vector<std::string_view>& path);
void printEnv(std::ostream& out, FCGX_Request& request);
std::string_view getPath(const FCGX_Request& request);
static bool info(Request* request);
static bool printEnvironment(Request* request);
private:
bool terminating;
uint64_t requestCount;
std::optional<std::string> serverName;
Router router;
};