261 lines
No EOL
7.2 KiB
C++
261 lines
No EOL
7.2 KiB
C++
#include <algorithm>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <thread>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
struct Image {
|
|
std::vector<uint8_t> bitmap_;
|
|
};
|
|
|
|
struct Credentail {
|
|
int cert_;
|
|
};
|
|
|
|
struct DownloadRequest {
|
|
enum class ConnectionType { Telnet,
|
|
Ssh };
|
|
|
|
std::string url_;
|
|
Credentail credentail_;
|
|
int maxMbps_;
|
|
bool cache_;
|
|
ConnectionType type_;
|
|
};
|
|
|
|
struct UploadRequest {
|
|
enum class ConnectionType { Telnet,
|
|
Ssh };
|
|
|
|
std::string url_;
|
|
Credentail credentail_;
|
|
int maxMbps_;
|
|
int size_;
|
|
ConnectionType type_;
|
|
};
|
|
|
|
class Command {
|
|
public:
|
|
enum class ErrorCode {
|
|
Ok,
|
|
WrongUrl,
|
|
CanNotConnect,
|
|
WrongCredential,
|
|
MaximumSizeExceeded,
|
|
MissingImage,
|
|
};
|
|
|
|
class Delegate {
|
|
public:
|
|
virtual ~Delegate() = default;
|
|
virtual ErrorCode removeFromCache(const std::string& url) = 0;
|
|
virtual void clearCache() = 0;
|
|
virtual void addToCache(const std::vector<uint8_t>& data, const std::string& url) = 0;
|
|
virtual ErrorCode download(std::vector<uint8_t>& data, const DownloadRequest& request) const = 0;
|
|
virtual ErrorCode upload(const std::vector<uint8_t>& data, const UploadRequest& request) const = 0;
|
|
};
|
|
|
|
explicit Command(Delegate* delegate)
|
|
: delegate_(delegate) {}
|
|
|
|
// Rule of 5!
|
|
virtual ~Command() = default;
|
|
Command(const Command&) = default;
|
|
Command(Command&&) = default;
|
|
Command& operator=(const Command&) = default;
|
|
Command& operator=(Command&&) = default;
|
|
|
|
virtual void operator()() const = 0;
|
|
|
|
protected:
|
|
Delegate* delegate_;
|
|
};
|
|
|
|
class DownloadImageCommand : public Command {
|
|
public:
|
|
using CallbackType = void (*)(ErrorCode, Image);
|
|
|
|
~DownloadImageCommand() override = default;
|
|
|
|
DownloadImageCommand(Delegate* delegate, CallbackType callback, const DownloadRequest& request)
|
|
: Command(delegate), callback_(callback), request_(request) {}
|
|
|
|
void operator()() const override {
|
|
std::vector<uint8_t> data;
|
|
if (auto ec = delegate_->download(data, request_); ec == Command::ErrorCode::Ok) {
|
|
// Do some conversion on vector
|
|
if (request_.cache_) {
|
|
delegate_->addToCache(data, request_.url_);
|
|
}
|
|
Image image{data};
|
|
callback_(ec, std::move(image));
|
|
} else {
|
|
callback_(ec, Image{});
|
|
}
|
|
};
|
|
|
|
private:
|
|
CallbackType callback_;
|
|
DownloadRequest request_;
|
|
};
|
|
|
|
class UploadImageCommand : public Command {
|
|
public:
|
|
using CallbackType = void (*)(ErrorCode);
|
|
|
|
~UploadImageCommand() override = default;
|
|
|
|
UploadImageCommand(Delegate* delegate, const Image& image, CallbackType callback, const UploadRequest& request)
|
|
: Command(delegate), image_(image), callback_(callback), request_(request) {}
|
|
|
|
void operator()() const override {
|
|
callback_(delegate_->upload(image_.bitmap_, request_));
|
|
};
|
|
|
|
private:
|
|
Image image_;
|
|
CallbackType callback_;
|
|
UploadRequest request_;
|
|
};
|
|
|
|
class Server : public Command::Delegate {
|
|
public:
|
|
~Server() override = default;
|
|
|
|
Command::ErrorCode removeFromCache(const std::string& url) override {
|
|
if (const auto it = findFile(url); it != std::cend(cache_)) {
|
|
cache_.erase(it);
|
|
return Command::ErrorCode::Ok;
|
|
}
|
|
|
|
return Command::ErrorCode::MissingImage;
|
|
}
|
|
|
|
void clearCache() override {
|
|
cache_.clear();
|
|
}
|
|
|
|
void addToCache(const std::vector<uint8_t>& data, const std::string& url) override {
|
|
cache_.emplace_back(data, url);
|
|
}
|
|
|
|
Command::ErrorCode download(std::vector<uint8_t>& data, const DownloadRequest& request) const override {
|
|
if (request.type_ == DownloadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 123) {
|
|
return Command::ErrorCode::WrongCredential;
|
|
}
|
|
if (request.type_ == DownloadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 231) {
|
|
return Command::ErrorCode::CanNotConnect;
|
|
}
|
|
|
|
// Simulate some other error
|
|
if (request.maxMbps_ % 2) {
|
|
return Command::ErrorCode::WrongUrl;
|
|
}
|
|
|
|
data = {97, 98, 99, 100, 101, 102};
|
|
return Command::ErrorCode::Ok;
|
|
}
|
|
|
|
Command::ErrorCode upload(const std::vector<uint8_t>& data, const UploadRequest& request) const override {
|
|
if (request.type_ == UploadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 1234) {
|
|
return Command::ErrorCode::WrongCredential;
|
|
}
|
|
if (request.type_ == UploadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 5432) {
|
|
return Command::ErrorCode::CanNotConnect;
|
|
}
|
|
|
|
// Simulate some other error
|
|
if (!request.maxMbps_ % 2) {
|
|
return Command::ErrorCode::WrongUrl;
|
|
}
|
|
return Command::ErrorCode::Ok;
|
|
}
|
|
|
|
private:
|
|
std::vector<std::pair<std::vector<uint8_t>, std::string>>::const_iterator findFile(const std::string& url) const {
|
|
return std::find_if(cbegin(cache_), cend(cache_),
|
|
[url](const auto& pair) {
|
|
const auto& [data, this_url] = pair;
|
|
return this_url == url;
|
|
});
|
|
}
|
|
|
|
std::vector<std::pair<std::vector<uint8_t>, std::string>> cache_;
|
|
};
|
|
|
|
class RequestHandler {
|
|
public:
|
|
void start() {
|
|
std::thread(&RequestHandler::run, this).detach();
|
|
}
|
|
|
|
void stop() {
|
|
stop_ = true;
|
|
cv_.notify_one();
|
|
}
|
|
|
|
void pushRequest(std::unique_ptr<Command> command) {
|
|
{
|
|
std::lock_guard lock(m_);
|
|
requests_.push(std::move(command));
|
|
}
|
|
cv_.notify_one();
|
|
}
|
|
|
|
private:
|
|
void run() {
|
|
while (!stop_) {
|
|
const auto request = waitForRequest();
|
|
if (!request || !*request) {
|
|
return;
|
|
}
|
|
|
|
(**request)();
|
|
}
|
|
}
|
|
|
|
std::optional<std::unique_ptr<Command>> waitForRequest() {
|
|
std::unique_lock lk(m_);
|
|
cv_.wait(lk, [&]() { return !requests_.empty() || stop_; });
|
|
if (stop_) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::unique_ptr<Command> request = std::move(requests_.front());
|
|
requests_.pop();
|
|
|
|
return std::move(request);
|
|
}
|
|
|
|
std::mutex m_;
|
|
std::condition_variable cv_;
|
|
std::atomic<bool> stop_{false};
|
|
std::queue<std::unique_ptr<Command>> requests_;
|
|
};
|
|
|
|
int main() {
|
|
Server server;
|
|
RequestHandler handler;
|
|
|
|
handler.start();
|
|
handler.pushRequest(std::make_unique<DownloadImageCommand>(
|
|
&server,
|
|
[](Command::ErrorCode ec, Image img) {
|
|
if (ec == Command::ErrorCode::Ok) {
|
|
std::copy(cbegin(img.bitmap_), cend(img.bitmap_), std::ostream_iterator<uint8_t>(std::cout, " "));
|
|
std::cout << '\n';
|
|
} else {
|
|
std::cout << "Sth went wrong!\n";
|
|
}
|
|
},
|
|
DownloadRequest{"Sth123", Credentail{123}, 200, true, DownloadRequest::ConnectionType::Ssh}));
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
handler.stop();
|
|
} |