refactor(file handle): attempt to decouple a bit
This commit is contained in:
parent
16841dd520
commit
72e3f962e4
@ -1,4 +1,5 @@
|
|||||||
#include "webserv/handler/ErrorHandler.hpp"
|
#include "webserv/handler/ErrorHandler.hpp"
|
||||||
|
#include "webserv/http/HttpConstants.hpp"
|
||||||
#include "webserv/http/HttpResponse.hpp"
|
#include "webserv/http/HttpResponse.hpp"
|
||||||
|
|
||||||
#include <webserv/config/LocationConfig.hpp>
|
#include <webserv/config/LocationConfig.hpp>
|
||||||
@ -8,15 +9,12 @@
|
|||||||
#include <webserv/log/Log.hpp>
|
#include <webserv/log/Log.hpp>
|
||||||
#include <webserv/utils/FileUtils.hpp>
|
#include <webserv/utils/FileUtils.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cerrno> // for errno
|
#include <cerrno> // for errno
|
||||||
#include <cstring> // for strerror, strlen
|
#include <cstring> // for strerror, strlen
|
||||||
#include <fstream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ranges> // for views
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
// for FILE, fopen, fclose, fread, SEEK_END, SEEK
|
|
||||||
#include <sys/stat.h> // for stat, S_ISREG, S_ISDIR
|
|
||||||
|
|
||||||
FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriParser)
|
FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriParser)
|
||||||
: location_(location), uriParser_(uriParser)
|
: location_(location), uriParser_(uriParser)
|
||||||
@ -24,102 +22,84 @@ FileHandler::FileHandler(const LocationConfig *location, const URIParser &uriPar
|
|||||||
Log::trace(LOCATION);
|
Log::trace(LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FileHandler::readBinaryFile(const std::string &filepath)
|
std::unique_ptr<HttpResponse> FileHandler::handleFile(const std::string &filepath) const
|
||||||
|
{
|
||||||
|
Log::trace(LOCATION);
|
||||||
|
auto response = std::make_unique<HttpResponse>();
|
||||||
|
|
||||||
|
std::string extension = FileUtils::getExtension(filepath);
|
||||||
|
std::string mimeType = MIMETypes().getType(extension);
|
||||||
|
response->addHeader("Content-Type", mimeType);
|
||||||
|
|
||||||
|
std::vector<char> fileData = FileUtils::readBinaryFile(filepath);
|
||||||
|
Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType);
|
||||||
|
if (fileData.empty())
|
||||||
|
{
|
||||||
|
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||||
|
}
|
||||||
|
// TODO: annoying: For reading files, vector<char> is preferred, but for http data vector<uint8_t> is preferred
|
||||||
|
response->setBody(std::vector<uint8_t>{fileData.begin(), fileData.end()});
|
||||||
|
response->setStatus(Http::StatusCode::OK);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<HttpResponse> FileHandler::handleDirectory(const std::string &dirpath, ResourceType type) const
|
||||||
{
|
{
|
||||||
Log::trace(LOCATION);
|
Log::trace(LOCATION);
|
||||||
|
|
||||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
if (type == DIRECTORY_INDEX)
|
||||||
if (!file.is_open())
|
|
||||||
{
|
{
|
||||||
Log::error("Failed to open file: " + filepath);
|
auto possible_indexes = location_->get<std::vector<std::string>>("index").value();
|
||||||
return {};
|
auto first_matching = std::ranges::find_if(possible_indexes, [&](const std::string &index) {
|
||||||
}
|
return FileUtils::isFile(FileUtils::joinPath(dirpath, index));
|
||||||
|
});
|
||||||
// Get file size
|
if (first_matching == possible_indexes.end())
|
||||||
std::streamsize size = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
// Read entire file
|
|
||||||
std::vector<char> buffer(size);
|
|
||||||
if (!file.read(buffer.data(), size))
|
|
||||||
{
|
{
|
||||||
Log::error("Failed to read file: " + filepath);
|
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, location_);
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
return handleFile(FileUtils::joinPath(dirpath, *first_matching));
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
if (type == DIRECTORY_AUTOINDEX)
|
||||||
std::string FileHandler::readFileAsString(const std::string &filepath)
|
|
||||||
{
|
{
|
||||||
Log::trace(LOCATION);
|
Log::debug("Requested path is a directory: " + dirpath);
|
||||||
|
return ErrorHandler::getErrorResponse(Http::StatusCode::FORBIDDEN, location_);
|
||||||
std::ifstream file(filepath, std::ios::binary);
|
|
||||||
if (!file.is_open())
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||||
return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<HttpResponse> FileHandler::getResponse() const
|
std::unique_ptr<HttpResponse> FileHandler::getResponse() const
|
||||||
{
|
{
|
||||||
Log::trace(LOCATION);
|
Log::trace(LOCATION);
|
||||||
auto response = std::make_unique<HttpResponse>();
|
|
||||||
response->setStatus(200);
|
|
||||||
std::string filepath = uriParser_.getFilePath();
|
std::string filepath = uriParser_.getFilePath();
|
||||||
|
ResourceType resourceType = getResourceType(filepath);
|
||||||
|
|
||||||
|
switch (resourceType)
|
||||||
|
{
|
||||||
|
case FILE: return handleFile(filepath);
|
||||||
|
case DIRECTORY_AUTOINDEX:
|
||||||
|
case DIRECTORY_INDEX: return handleDirectory(filepath, resourceType);
|
||||||
|
case NOT_FOUND: return ErrorHandler::getErrorResponse(Http::StatusCode::NOT_FOUND, location_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileHandler::ResourceType FileHandler::getResourceType(const std::string &path) const
|
||||||
|
{
|
||||||
|
Log::trace(LOCATION);
|
||||||
|
|
||||||
if (uriParser_.isFile())
|
if (uriParser_.isFile())
|
||||||
{
|
{
|
||||||
std::string extension = uriParser_.getExtension();
|
return FILE;
|
||||||
std::string mimeType = MIMETypes().getType(extension);
|
}
|
||||||
response->addHeader("Content-Type", mimeType);
|
if (uriParser_.isDirectory())
|
||||||
|
|
||||||
std::vector<char> fileData = readBinaryFile(filepath);
|
|
||||||
Log::debug("Serving file: " + filepath + " with MIME type: " + mimeType);
|
|
||||||
if (fileData.empty())
|
|
||||||
{
|
{
|
||||||
return ErrorHandler::getErrorResponse(404, location_);
|
if (location_->get<std::vector<std::string>>("index").has_value())
|
||||||
}
|
|
||||||
response->setBody(std::string(fileData.begin(), fileData.end()));
|
|
||||||
}
|
|
||||||
else if (uriParser_.isDirectory() && location_->get<std::vector<std::string>>("index").has_value())
|
|
||||||
{
|
{
|
||||||
auto possible_indexes = location_->get<std::vector<std::string>>("index").value();
|
return DIRECTORY_INDEX;
|
||||||
std::string indexPath;
|
}
|
||||||
for (auto &index : possible_indexes)
|
if (location_->get<bool>("autoindex").value_or(false))
|
||||||
{
|
{
|
||||||
indexPath = FileUtils::joinPath(filepath, index);
|
return DIRECTORY_AUTOINDEX;
|
||||||
Log::debug("Checking for index file: " + indexPath);
|
|
||||||
if (FileUtils::isFile(indexPath))
|
|
||||||
{
|
|
||||||
Log::debug("Found index file: " + indexPath);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
indexPath.clear();
|
|
||||||
}
|
}
|
||||||
|
return NOT_FOUND;
|
||||||
std::vector<char> fileData = readBinaryFile(indexPath);
|
|
||||||
Log::debug("Serving index file: " + indexPath);
|
|
||||||
if (fileData.empty())
|
|
||||||
{
|
|
||||||
return ErrorHandler::getErrorResponse(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string extension = FileUtils::getExtension(indexPath);
|
|
||||||
std::string mimeType = MIMETypes().getType(extension);
|
|
||||||
response->addHeader("Content-Type", mimeType);
|
|
||||||
response->setBody(std::string(fileData.begin(), fileData.end()));
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (uriParser_.isDirectory() && location_->get<bool>("autoindex").value_or(false))
|
|
||||||
{
|
|
||||||
Log::debug("Requested path is a directory: " + filepath);
|
|
||||||
return ErrorHandler::getErrorResponse(403);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ErrorHandler::getErrorResponse(404);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
@ -4,8 +4,9 @@
|
|||||||
#include "webserv/handler/URIParser.hpp"
|
#include "webserv/handler/URIParser.hpp"
|
||||||
#include "webserv/http/HttpResponse.hpp"
|
#include "webserv/http/HttpResponse.hpp"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class FileHandler
|
class FileHandler
|
||||||
{
|
{
|
||||||
@ -13,11 +14,20 @@ class FileHandler
|
|||||||
FileHandler(const LocationConfig *location, const URIParser &uriParser);
|
FileHandler(const LocationConfig *location, const URIParser &uriParser);
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<HttpResponse> getResponse() const;
|
[[nodiscard]] std::unique_ptr<HttpResponse> getResponse() const;
|
||||||
static std::vector<char> readBinaryFile(const std ::string &filepath);
|
|
||||||
static std::string readFileAsString(const std::string &filepath);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// std::unique_ptr<HttpResponse> response_;
|
|
||||||
const LocationConfig *location_;
|
const LocationConfig *location_;
|
||||||
const URIParser &uriParser_;
|
const URIParser &uriParser_;
|
||||||
|
|
||||||
|
enum ResourceType : uint8_t
|
||||||
|
{
|
||||||
|
FILE,
|
||||||
|
DIRECTORY_INDEX,
|
||||||
|
DIRECTORY_AUTOINDEX,
|
||||||
|
NOT_FOUND
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] ResourceType getResourceType(const std::string &path) const;
|
||||||
|
[[nodiscard]] std::unique_ptr<HttpResponse> handleFile(const std::string &filepath) const;
|
||||||
|
[[nodiscard]] std::unique_ptr<HttpResponse> handleDirectory(const std::string &dirpath, ResourceType type) const;
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user