234 lines
No EOL
6.2 KiB
C++
234 lines
No EOL
6.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 Credentail {
|
|
int cert_;
|
|
};
|
|
|
|
struct Download {
|
|
enum class ConnectionType { Telnet,
|
|
Ssh };
|
|
|
|
std::string url_;
|
|
Credentail credentail_;
|
|
int maxMbps_;
|
|
bool cache_;
|
|
ConnectionType type_;
|
|
};
|
|
|
|
struct Upload {
|
|
enum class ConnectionType { Telnet,
|
|
Ssh };
|
|
|
|
std::string url_;
|
|
Credentail credentail_;
|
|
int maxMbps_;
|
|
int size_;
|
|
ConnectionType type_;
|
|
};
|
|
|
|
struct RemoveFromCache {
|
|
std::string url_;
|
|
};
|
|
|
|
struct ClearCache {
|
|
};
|
|
|
|
struct Image {
|
|
std::vector<uint8_t> bitmap_;
|
|
};
|
|
|
|
class Server {
|
|
public:
|
|
enum class ErrorCode {
|
|
Ok,
|
|
WrongUrl,
|
|
CanNotConnect,
|
|
WrongCredential,
|
|
MaximumSizeExceeded,
|
|
MissingImage,
|
|
};
|
|
using CallbackType = void (*)(ErrorCode, Image);
|
|
|
|
void handle(const Download& request, CallbackType callback) {
|
|
if (const auto it = findImage(request.url_); it != std::cend(cache_)) {
|
|
callback(ErrorCode::Ok, it->first);
|
|
return;
|
|
}
|
|
|
|
Image img;
|
|
const auto ec = download(request, img);
|
|
if (ec == ErrorCode::Ok) {
|
|
if (request.cache_) {
|
|
cache_.emplace_back(img, request.url_);
|
|
}
|
|
}
|
|
|
|
callback(ec, img);
|
|
}
|
|
|
|
void handle(const Upload& request, CallbackType callback) const {
|
|
if (request.size_ > 100) {
|
|
callback(ErrorCode::MaximumSizeExceeded, Image{});
|
|
return;
|
|
}
|
|
|
|
Image img;
|
|
callback(upload(request, img), img);
|
|
}
|
|
|
|
void handle(const RemoveFromCache& request, CallbackType callback) {
|
|
if (const auto it = findImage(request.url_); it != std::cend(cache_)) {
|
|
cache_.erase(it);
|
|
callback(ErrorCode::Ok, Image{});
|
|
return;
|
|
}
|
|
|
|
callback(ErrorCode::MissingImage, Image{});
|
|
}
|
|
|
|
void handle(const ClearCache& request, CallbackType callback) {
|
|
cache_.clear();
|
|
callback(ErrorCode::Ok, Image{});
|
|
}
|
|
|
|
private:
|
|
std::vector<std::pair<Image, std::string>>::const_iterator findImage(const std::string& url) const {
|
|
return std::find_if(cbegin(cache_), cend(cache_),
|
|
[url](const auto& pair) {
|
|
const auto& [image, this_url] = pair;
|
|
return this_url == url;
|
|
});
|
|
}
|
|
|
|
ErrorCode download(const Download& request, Image& image) const {
|
|
if (request.type_ == Download::ConnectionType::Ssh && request.credentail_.cert_ != 123) {
|
|
return ErrorCode::WrongCredential;
|
|
}
|
|
if (request.type_ == Download::ConnectionType::Telnet && request.credentail_.cert_ != 231) {
|
|
return ErrorCode::CanNotConnect;
|
|
}
|
|
|
|
// Simulate some other error
|
|
if (request.maxMbps_ % 2) {
|
|
return ErrorCode::WrongUrl;
|
|
}
|
|
image.bitmap_ = {97, 98, 99, 100, 101, 102};
|
|
return ErrorCode::Ok;
|
|
}
|
|
|
|
ErrorCode upload(const Upload& request, const Image& image) const {
|
|
if (request.type_ == Upload::ConnectionType::Ssh && request.credentail_.cert_ != 1234) {
|
|
return ErrorCode::WrongCredential;
|
|
}
|
|
if (request.type_ == Upload::ConnectionType::Telnet && request.credentail_.cert_ != 5432) {
|
|
return ErrorCode::CanNotConnect;
|
|
}
|
|
|
|
// Simulate some other error
|
|
if (!request.maxMbps_ % 2) {
|
|
return ErrorCode::WrongUrl;
|
|
}
|
|
return ErrorCode::Ok;
|
|
}
|
|
|
|
std::vector<std::pair<Image, std::string>> cache_;
|
|
};
|
|
|
|
class RequestHandler {
|
|
public:
|
|
using RequestType = std::variant<Download, Upload, RemoveFromCache, ClearCache>;
|
|
|
|
void start(Server* server) {
|
|
std::thread(&RequestHandler::run, this, server).detach();
|
|
}
|
|
|
|
void stop() {
|
|
stop_ = true;
|
|
cv_.notify_one();
|
|
}
|
|
|
|
void pushRequest(const RequestType& request, Server::CallbackType callback) {
|
|
{
|
|
std::lock_guard lock(m_);
|
|
requests_.emplace(request, callback);
|
|
}
|
|
cv_.notify_one();
|
|
}
|
|
|
|
private:
|
|
using QueueType = std::pair<RequestType, Server::CallbackType>;
|
|
|
|
void run(Server* server) {
|
|
while (!stop_) {
|
|
const auto res = waitForRequest();
|
|
if (!res) {
|
|
return;
|
|
}
|
|
|
|
const auto [request, callback] = *res;
|
|
switch (request.index()) {
|
|
case 0:
|
|
server->handle(std::get<Download>(request), callback);
|
|
break;
|
|
case 1:
|
|
server->handle(std::get<Upload>(request), callback);
|
|
break;
|
|
case 2:
|
|
server->handle(std::get<RemoveFromCache>(request), callback);
|
|
break;
|
|
case 3:
|
|
server->handle(std::get<ClearCache>(request), callback);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::optional<QueueType> waitForRequest() {
|
|
std::unique_lock lk(m_);
|
|
cv_.wait(lk, [&]() { return !requests_.empty() || stop_; });
|
|
if (stop_) {
|
|
return std::nullopt;
|
|
}
|
|
auto pair = requests_.front();
|
|
requests_.pop();
|
|
|
|
return pair;
|
|
}
|
|
|
|
std::mutex m_;
|
|
std::condition_variable cv_;
|
|
std::atomic<bool> stop_{false};
|
|
std::queue<QueueType> requests_;
|
|
};
|
|
|
|
int main() {
|
|
Server server;
|
|
RequestHandler handler;
|
|
|
|
handler.start(&server);
|
|
|
|
handler.pushRequest(Download{"sth.png", 123, 100, true, Download::ConnectionType::Ssh},
|
|
[](Server::ErrorCode ec, Image image) {
|
|
if (ec == Server::ErrorCode::Ok) {
|
|
for (auto el : image.bitmap_) {
|
|
std::cout << el << ' ';
|
|
}
|
|
std::cout << '\n';
|
|
} else {
|
|
std::cout << "FAILED!\n";
|
|
}
|
|
});
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
handler.stop();
|
|
} |