first couple of requests
This commit is contained in:
parent
68e795f0e6
commit
aae7873d67
@ -9,6 +9,7 @@ cmake_policy(SET CMP0076 NEW)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(FCGI fcgi)
|
pkg_check_modules(FCGI fcgi)
|
||||||
|
|
||||||
@ -20,9 +21,10 @@ add_subdirectory(request)
|
|||||||
add_subdirectory(response)
|
add_subdirectory(response)
|
||||||
add_subdirectory(stream)
|
add_subdirectory(stream)
|
||||||
|
|
||||||
target_link_libraries(pica
|
target_link_libraries(pica PRIVATE
|
||||||
fcgi
|
fcgi
|
||||||
fcgi++
|
fcgi++
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS pica RUNTIME DESTINATION bin)
|
install(TARGETS pica RUNTIME DESTINATION bin)
|
||||||
|
2
main.cpp
2
main.cpp
@ -15,7 +15,7 @@ int main() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chmod(socketPath, 0770) != 0) {
|
if (chmod(socketPath, 0777) != 0) {
|
||||||
std::cerr << "Couldn't set socket permissions" << std::endl;
|
std::cerr << "Couldn't set socket permissions" << std::endl;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
@ -63,16 +63,23 @@ OStream Request::getErrorStream() {
|
|||||||
return OStream(raw.err);
|
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)
|
if (state != State::accepted)
|
||||||
throw std::runtime_error("An attempt to request path on a wrong request state");
|
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_view scriptFileName(FCGX_GetParam(SCRIPT_FILENAME, raw.envp));
|
||||||
std::string::size_type snLocation = scriptFileName.find(serverName);
|
std::string::size_type snLocation = scriptFileName.find(serverName);
|
||||||
|
|
||||||
if (snLocation != std::string::npos) {
|
if (snLocation != std::string::npos) {
|
||||||
if (snLocation + serverName.size() < scriptFileName.size())
|
if (snLocation + serverName.size() < scriptFileName.size())
|
||||||
path = scriptFileName.substr(snLocation + serverName.size() + 1);
|
path = scriptFileName.substr(snLocation + serverName.size() + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.empty()) {
|
||||||
|
while (path.back() == '/')
|
||||||
|
path.erase(path.end() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
@ -88,6 +95,19 @@ std::string Request::getServerName() const {
|
|||||||
void Request::printEnvironment(std::ostream& out) {
|
void Request::printEnvironment(std::ostream& out) {
|
||||||
char **envp = raw.envp;
|
char **envp = raw.envp;
|
||||||
for (int i = 0; envp[i] != nullptr; ++i) {
|
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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <fcgiapp.h>
|
#include <fcgiapp.h>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "stream/ostream.h"
|
#include "stream/ostream.h"
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
@ -30,9 +32,10 @@ public:
|
|||||||
OStream getOutputStream();
|
OStream getOutputStream();
|
||||||
OStream getErrorStream();
|
OStream getErrorStream();
|
||||||
|
|
||||||
std::string_view getPath(const std::string& serverName) const;
|
std::string getPath(const std::string& serverName) const;
|
||||||
std::string getServerName() const;
|
std::string getServerName() const;
|
||||||
void printEnvironment(std::ostream& out);
|
void printEnvironment(std::ostream& out);
|
||||||
|
void printEnvironment(nlohmann::json& out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
State state;
|
State state;
|
||||||
|
@ -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 = {
|
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():
|
Response::Response():
|
||||||
@ -38,5 +39,11 @@ void Response::replyTo(Request& request) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Response::setBody(const std::string& body) {
|
void Response::setBody(const std::string& body) {
|
||||||
|
type = ContentType::text;
|
||||||
Response::body = body;
|
Response::body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Response::setBody(const nlohmann::json& body) {
|
||||||
|
type = ContentType::json;
|
||||||
|
Response::body = body.dump();
|
||||||
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "request/request.h"
|
#include "request/request.h"
|
||||||
#include "stream/ostream.h"
|
#include "stream/ostream.h"
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ public:
|
|||||||
|
|
||||||
enum class ContentType {
|
enum class ContentType {
|
||||||
text,
|
text,
|
||||||
|
json,
|
||||||
__size
|
__size
|
||||||
};
|
};
|
||||||
Response();
|
Response();
|
||||||
@ -26,6 +29,7 @@ public:
|
|||||||
|
|
||||||
void replyTo(Request& request) const;
|
void replyTo(Request& request) const;
|
||||||
void setBody(const std::string& body);
|
void setBody(const std::string& body);
|
||||||
|
void setBody(const nlohmann::json& body);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Status status;
|
Status status;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
set(HEADERS
|
set(HEADERS
|
||||||
server.h
|
server.h
|
||||||
|
router.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
server.cpp
|
server.cpp
|
||||||
|
router.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(pica PRIVATE ${SOURCES})
|
||||||
|
41
server/router.cpp
Normal file
41
server/router.cpp
Normal 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
27
server/router.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
@ -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_NAME("SCRIPT_NAME");
|
||||||
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
|
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
|
||||||
|
|
||||||
constexpr std::string_view info("info");
|
|
||||||
constexpr std::string_view env("env");
|
|
||||||
|
|
||||||
Server::Server():
|
Server::Server():
|
||||||
terminating(false),
|
terminating(false),
|
||||||
requestCount(0),
|
requestCount(0),
|
||||||
serverName(std::nullopt)
|
serverName(std::nullopt),
|
||||||
{}
|
router()
|
||||||
|
{
|
||||||
|
router.addRoute("info", Server::info);
|
||||||
|
router.addRoute("env", Server::printEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
Server::~Server() {}
|
Server::~Server() {}
|
||||||
|
|
||||||
@ -52,27 +53,34 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::string_view sPath = request->getPath(serverName.value());
|
std::string path = request->getPath(serverName.value());
|
||||||
|
router.route(path.data(), std::move(request));
|
||||||
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;
|
|
||||||
}
|
|
||||||
} catch (const std::exception e) {
|
} catch (const std::exception e) {
|
||||||
Response error(Response::Status::internalError);
|
Response error(Response::Status::internalError);
|
||||||
error.setBody(e.what());
|
error.setBody(std::string(e.what()));
|
||||||
error.replyTo(*request.get());
|
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;
|
||||||
|
}
|
||||||
|
@ -12,8 +12,11 @@
|
|||||||
#include <fcgio.h>
|
#include <fcgio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include "request/request.h"
|
#include "request/request.h"
|
||||||
#include "response/response.h"
|
#include "response/response.h"
|
||||||
|
#include "router.h"
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
public:
|
public:
|
||||||
@ -24,12 +27,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void handleRequest(std::unique_ptr<Request> request);
|
void handleRequest(std::unique_ptr<Request> request);
|
||||||
void router(const std::vector<std::string_view>& path);
|
|
||||||
void printEnv(std::ostream& out, FCGX_Request& request);
|
static bool info(Request* request);
|
||||||
std::string_view getPath(const FCGX_Request& request);
|
static bool printEnvironment(Request* request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool terminating;
|
bool terminating;
|
||||||
uint64_t requestCount;
|
uint64_t requestCount;
|
||||||
std::optional<std::string> serverName;
|
std::optional<std::string> serverName;
|
||||||
|
Router router;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user