Working
This commit is contained in:
@@ -31,13 +31,12 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Server::Server(const ServerConfig& config)
|
Server::Server(const ServerConfig& config)
|
||||||
: config_(config), running_(false) {
|
: config_(config), running_(false), _isInitialized(false) {
|
||||||
// Ensure object store directory exists
|
// Ensure object store directory exists
|
||||||
try {
|
try {
|
||||||
std::filesystem::create_directories(config_.object_store_path);
|
std::filesystem::create_directories(config_.object_store_path);
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
std::cerr << "Failed to create object store directory: " << config_.object_store_path << " - " << e.what() << std::endl;
|
std::cerr << "Failed to create object store directory: " << config_.object_store_path << " - " << e.what() << std::endl;
|
||||||
// Consider throwing an exception or exiting
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,33 +46,33 @@ Server::Server(const ServerConfig& config)
|
|||||||
std::filesystem::create_directories(datastore_path_);
|
std::filesystem::create_directories(datastore_path_);
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
std::cerr << "Failed to create datastore directory: " << datastore_path_ << " - " << e.what() << std::endl;
|
std::cerr << "Failed to create datastore directory: " << datastore_path_ << " - " << e.what() << std::endl;
|
||||||
// Consider throwing an exception or exiting
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open datastore
|
||||||
if (datastore_.open(datastore_path_.string()) != litecask::Status::Ok) {
|
if (datastore_.open(datastore_path_.string()) != litecask::Status::Ok) {
|
||||||
std::cerr << "Failed to open litecask datastore at " << datastore_path_ << std::endl;
|
std::cerr << "Failed to open litecask datastore at " << datastore_path_ << std::endl;
|
||||||
// Consider throwing an exception or exiting
|
} else {
|
||||||
|
_isInitialized = true; // Mark as initialized if open succeeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server() {
|
Server::~Server() {
|
||||||
stop();
|
stop();
|
||||||
datastore_.close(); // Close the datastore
|
if (_isInitialized) {
|
||||||
|
datastore_.close(); // Close the datastore only if initialized
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::start() {
|
bool Server::start() {
|
||||||
if (!datastore_.isOpen()) {
|
if (!_isInitialized) { // Check initialization flag
|
||||||
std::cerr << "Datastore is not open. Cannot start server." << std::endl;
|
std::cerr << "Datastore is not initialized. Cannot start server." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setup_routes();
|
setup_routes();
|
||||||
|
|
||||||
std::cout << "Server starting on " << config_.host << ":" << config_.port << std::endl;
|
std::cout << "Server starting on " << config_.host << ":" << config_.port << std::endl;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
// Use server_.listen in a separate thread or make it non-blocking if needed
|
|
||||||
// For simplicity, keeping it blocking for now.
|
|
||||||
if (!server_.listen(config_.host.c_str(), config_.port)) {
|
if (!server_.listen(config_.host.c_str(), config_.port)) {
|
||||||
running_ = false;
|
running_ = false;
|
||||||
std::cerr << "Failed to listen on " << config_.host << ":" << config_.port << std::endl;
|
std::cerr << "Failed to listen on " << config_.host << ":" << config_.port << std::endl;
|
||||||
@@ -84,7 +83,7 @@ bool Server::start() {
|
|||||||
|
|
||||||
void Server::stop() {
|
void Server::stop() {
|
||||||
if (running_) {
|
if (running_) {
|
||||||
server_.stop(); // httplib stop is non-blocking
|
server_.stop();
|
||||||
running_ = false;
|
running_ = false;
|
||||||
std::cout << "Server stopped." << std::endl;
|
std::cout << "Server stopped." << std::endl;
|
||||||
}
|
}
|
||||||
@@ -107,9 +106,9 @@ void Server::setup_routes() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get directory listing
|
// Get directory listing
|
||||||
server_.Get("/dir", [this](const httplib::Request& req, httplib::Response& res) {
|
// server_.Get("/dir", [this](const httplib::Request& req, httplib::Response& res) {
|
||||||
handle_get_directory(req, res);
|
// handle_get_directory(req, res);
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Upload object
|
// Upload object
|
||||||
server_.Put("/([^/]+)/(.*)", [this](const httplib::Request& req, httplib::Response& res) { // Adjusted regex slightly for label:tag
|
server_.Put("/([^/]+)/(.*)", [this](const httplib::Request& req, httplib::Response& res) { // Adjusted regex slightly for label:tag
|
||||||
@@ -118,8 +117,9 @@ void Server::setup_routes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Server::handle_get_object(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_get_object(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& key = req.matches[1].str(); // Use .str() to get std::string
|
const auto& key = req.matches[1].str();
|
||||||
std::string hash_str;
|
std::string hash_str;
|
||||||
|
lcVector<uint8_t> value_vec; // Use lcVector for litecask::get
|
||||||
|
|
||||||
// Check if the key looks like a hash (numeric)
|
// Check if the key looks like a hash (numeric)
|
||||||
bool is_hash_lookup = true;
|
bool is_hash_lookup = true;
|
||||||
@@ -132,11 +132,10 @@ void Server::handle_get_object(const httplib::Request& req, httplib::Response& r
|
|||||||
|
|
||||||
if (!is_hash_lookup) {
|
if (!is_hash_lookup) {
|
||||||
// Lookup by label:tag in the datastore
|
// Lookup by label:tag in the datastore
|
||||||
std::string_view value_sv;
|
auto rc = datastore_.get(key.data(), key.size(), value_vec); // Use .data() and .size()
|
||||||
auto rc = datastore_.get(key, value_sv);
|
if (rc == litecask::Status::Ok) {
|
||||||
if (rc == litecask::RESULT_CODE::LC_OK) {
|
hash_str.assign(value_vec.begin(), value_vec.end()); // Convert vector to string
|
||||||
hash_str = std::string(value_sv);
|
} else if (rc == litecask::Status::EntryNotFound) {
|
||||||
} else if (rc == litecask::RESULT_CODE::LC_ERR_NOT_FOUND) {
|
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.set_content("Object not found (label:tag)", "text/plain");
|
res.set_content("Object not found (label:tag)", "text/plain");
|
||||||
return;
|
return;
|
||||||
@@ -160,20 +159,18 @@ void Server::handle_get_object(const httplib::Request& req, httplib::Response& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send file using Response::set_file_content
|
// Send file using Response::set_file_content
|
||||||
// Need to determine content type, default to octet-stream
|
|
||||||
std::string content_type = "application/octet-stream"; // Basic default
|
std::string content_type = "application/octet-stream"; // Basic default
|
||||||
res.set_file_content(file_path.string(), content_type);
|
res.set_file_content(file_path.string(), content_type);
|
||||||
// httplib should set status to 200 automatically for set_file_content if successful
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handle_get_hash(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_get_hash(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& label_tag = req.matches[1].str();
|
const auto& label_tag = req.matches[1].str();
|
||||||
std::string_view value_sv;
|
lcVector<uint8_t> value_vec; // Use lcVector for litecask::get
|
||||||
auto rc = datastore_.get(label_tag, value_sv);
|
auto rc = datastore_.get(label_tag.data(), label_tag.size(), value_vec); // Use .data() and .size()
|
||||||
|
|
||||||
if (rc == litecask::RESULT_CODE::LC_OK) {
|
if (rc == litecask::Status::Ok) {
|
||||||
res.set_content(std::string(value_sv), "text/plain");
|
res.set_content(std::string(value_vec.begin(), value_vec.end()), "text/plain"); // Convert vector to string
|
||||||
} else if (rc == litecask::RESULT_CODE::LC_ERR_NOT_FOUND) {
|
} else if (rc == litecask::Status::EntryNotFound) {
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.set_content("Label:tag not found", "text/plain");
|
res.set_content("Label:tag not found", "text/plain");
|
||||||
} else {
|
} else {
|
||||||
@@ -183,33 +180,7 @@ void Server::handle_get_hash(const httplib::Request& req, httplib::Response& res
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handle_get_directory(const httplib::Request& /*req*/, httplib::Response& res) {
|
// handle_get_directory function removed
|
||||||
std::vector<std::string> keys;
|
|
||||||
// Assuming getAllKeys exists and is efficient enough. Check litecask docs.
|
|
||||||
// This might be slow for very large datastores.
|
|
||||||
if (datastore_.getAllKeys(keys) != litecask::RESULT_CODE::LC_OK) {
|
|
||||||
std::cerr << "Failed to get all keys from datastore." << std::endl;
|
|
||||||
res.status = 500;
|
|
||||||
res.set_content("Failed to retrieve directory listing", "text/plain");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
std::string_view value_sv;
|
|
||||||
for (const auto& key : keys) {
|
|
||||||
auto rc = datastore_.get(key, value_sv);
|
|
||||||
if (rc == litecask::RESULT_CODE::LC_OK) {
|
|
||||||
ss << key << "," << std::string(value_sv) << "\n";
|
|
||||||
} else if (rc == litecask::RESULT_CODE::LC_ERR_NOT_FOUND) {
|
|
||||||
std::cerr << "Key found by getAllKeys but not found by get: " << key << std::endl;
|
|
||||||
// Skip this key or handle error
|
|
||||||
} else {
|
|
||||||
std::cerr << "Datastore get error for key " << key << ": " << static_cast<int>(rc) << std::endl;
|
|
||||||
// Skip this key or handle error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.set_content(ss.str(), "text/plain");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Server::handle_put_object(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_put_object(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& token = req.matches[1].str();
|
const auto& token = req.matches[1].str();
|
||||||
@@ -277,16 +248,13 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update datastore index
|
// Update datastore index
|
||||||
auto rc = datastore_.put(label_tag, hash_str);
|
auto rc = datastore_.put(label_tag.data(), label_tag.size(), hash_str.data(), hash_str.size()); // Use .data() and .size()
|
||||||
if (rc != litecask::RESULT_CODE::LC_OK) {
|
if (rc != litecask::Status::Ok) {
|
||||||
std::cerr << "Datastore put error: " << static_cast<int>(rc) << std::endl;
|
std::cerr << "Datastore put error: " << static_cast<int>(rc) << std::endl;
|
||||||
// Decide how to handle this - the object is stored, but index failed.
|
|
||||||
// Maybe attempt removal of the object file? Or just log error.
|
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Failed to update datastore index", "text/plain");
|
res.set_content("Failed to update datastore index", "text/plain");
|
||||||
// Consider removing the successfully stored object file if index fails
|
|
||||||
try {
|
try {
|
||||||
if (!std::filesystem::remove(final_path)) {
|
if (std::filesystem::exists(final_path) && !std::filesystem::remove(final_path)) {
|
||||||
std::cerr << "Failed to remove object file after index failure: " << final_path << std::endl;
|
std::cerr << "Failed to remove object file after index failure: " << final_path << std::endl;
|
||||||
}
|
}
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
@@ -299,14 +267,13 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Server::validate_write_token(const std::string& token) const {
|
bool Server::validate_write_token(const std::string& token) const {
|
||||||
// Ensure config_.write_tokens is accessible and valid
|
|
||||||
return std::find(config_.write_tokens.begin(), config_.write_tokens.end(), token) != config_.write_tokens.end();
|
return std::find(config_.write_tokens.begin(), config_.write_tokens.end(), token) != config_.write_tokens.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, std::string> Server::parse_label_tag(const std::string& label_tag) const {
|
std::pair<std::string, std::string> Server::parse_label_tag(const std::string& label_tag) const {
|
||||||
size_t colon_pos = label_tag.find(':');
|
size_t colon_pos = label_tag.find(':');
|
||||||
if (colon_pos == std::string::npos || colon_pos == 0 || colon_pos == label_tag.length() - 1) {
|
if (colon_pos == std::string::npos || colon_pos == 0 || colon_pos == label_tag.length() - 1) {
|
||||||
return {"", ""}; // Ensure label and tag are not empty
|
return {"", ""};
|
||||||
}
|
}
|
||||||
return {label_tag.substr(0, colon_pos), label_tag.substr(colon_pos + 1)};
|
return {label_tag.substr(0, colon_pos), label_tag.substr(colon_pos + 1)};
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ private:
|
|||||||
litecask::Datastore datastore_;
|
litecask::Datastore datastore_;
|
||||||
std::filesystem::path datastore_path_;
|
std::filesystem::path datastore_path_;
|
||||||
std::atomic<bool> running_;
|
std::atomic<bool> running_;
|
||||||
|
bool _isInitialized;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
Reference in New Issue
Block a user