#pragma once
#include
#include
#include
#include ../common/bytevec.h
/// Storage is the main object managed by the server.It provides access to two
/// concurrent maps.The first is an authentication table.The authentication
/// table holds user names, salts, and hashed passwords, as well as a single
/// bytevec of content per user.The second is a key/value store that can be
/// accessed by all threads.
///
/// The public interface of Storage provides functions that correspond 1:1 with
/// the data requests that a client can make.In that manner, the server
/// command handlers need only parse a request, send its parts to the Storage
/// object, and then format and return the result.
///
/// Note that the functions that correspond to client requests all return a
/// tuple, consisting of a bool, a string, and a bytevec.When the bool is
/// *false*, it means that the operation did not succeed, and the string is an
/// error message that can be sent to the client.When the bool is *true*, it
/// means that the operation succeeded.In this case the string is mostlikely
/// RES_OK, and the bytevec, if not empty, is additional data to send to the
/// client.
class Storage {
/// Internal is the class that stores all the members of a Storage object.To
/// avoid pulling too much into the .h file, we are using the PIMPL pattern
/// (https://www.geeksforgeeks.org/pimpl-idiom-in-c-with-examples/)
struct Internal;
/// A reference to the internal fields of the Storage object
std::unique_ptr
public:
/// Result_t is the tuple that is sent to the caller after any operation
/// requested by a client.
struct result_t {
bool succeeded;// True if the operation succeeded, false otherwise
std::string msg; // The message to send to the client
bytevec data;// Optional additional content to return to the client
};
/// Construct an empty object and specify the file from which it should be
/// loaded.To avoid exceptions and errors in the constructor, the act of
/// loading data is separate from construction.
///
/// @param fname The name of the file to use for persistence
/// @param buckets The number of buckets in the hash table
Storage(const std::string &fname, size_t buckets);
/// Destructor for the storage object.
~Storage();
/// Populate the Storage object by loading this.filename.Note that
/// load_file() begins by clearing the maps, so that when the call is
/// complete, exactly and only the contents of the file are in the Storage
/// object.
///
/// @return A result tuple, as described above.Note that a non-existent file
/// is not an error.
result_t load_file();
/// Create a new entry in the Auth table.If the user already exists, return
/// an error.Otherwise, create a salt, hash the password, and then save an
/// entry with the username, salt, hashed password, and a zero-byte content.
///
/// @param user The user name to register
/// @param pass The password to associate with that user name
///
/// @return A result tuple, as described above
result_t add_user(const std::string &user, const std::string &pass);
/// Set the data bytes for a user, but do so if and only if the password
/// matches
///
/// @param userThe name of the user whose content is being set
/// @param passThe password for the user, used to authenticate
/// @param content The data to set for this user
///
/// @return A result tuple, as described above
result_t set_user_data(const std::string &user, const std::string &pass,
const bytevec &content);
/// Return a copy of the user data for a user, but do so only if the password
/// matches
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
/// @param whoThe name of the user whose content is being fetched
///
/// @return A result tuple, as described above.Note that no data is an
/// error
result_t get_user_data(const std::string &user, const std::string &pass,
const std::string &who);
/// Return a newline-delimited string containing all of the usernames in the
/// auth table
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
///
/// @return A result tuple, as described above
result_t get_all_users(const std::string &user, const std::string &pass);
/// Authenticate a user
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
///
/// @return A result tuple, as described above
result_t auth(const std::string &user, const std::string &pass);
/// Write the entire Storage object to the file specified by this.filename.
/// To ensure durability, Storage must be persisted in two steps.First, it
/// must be written to a temporary file (this.filename.tmp).Then the
/// temporary file can be renamed to replace the older version of the Storage
/// object.
///
/// @return A result tuple, as described above
result_t save_file();
/// Create a new key/value mapping in the table
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
/// @param keyThe key whose mapping is being created
/// @param valThe value to copy into the map
///
/// @return A result tuple, as described above
result_t kv_insert(const std::string &user, const std::string &pass,
const std::string &key, const bytevec &val);
/// Get a copy of the value to which a key is mapped
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
/// @param keyThe key whose value is being fetched
///
/// @return A result tuple, as described above
result_t kv_get(const std::string &user, const std::string &pass,
const std::string &key);
/// Delete a key/value mapping
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
/// @param keyThe key whose value is being deleted
///
/// @return A result tuple, as described above
result_t kv_delete(const std::string &user, const std::string &pass,
const std::string &key);
/// Insert or update, so that the given key is mapped to the give value
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
/// @param keyThe key whose mapping is being upserted
/// @param valThe value to copy into the map
///
/// @return A result tuple, as described above.Note that there are two OK
/// messages, depending on whether we get an insert or an update.
result_t kv_upsert(const std::string &user, const std::string &pass,
const std::string &key, const bytevec &val);
/// Return all of the keys in the kv_store, as a
-delimited string
///
/// @param user The name of the user who made the request
/// @param pass The password for the user, used to authenticate
///
/// @return A result tuple, as described above
result_t kv_all(const std::string &user, const std::string &pass);
/// Shut down the storage when the server stops.This method needs to close
/// any open files related to incremental persistence.It also needs to clean
/// up any state related to .so files.This is only called when all threads
/// have stopped accessing the Storage object.
void shutdown();
};
Reviews
There are no reviews yet.