Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 135 additions & 101 deletions src/api_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,129 +14,163 @@
limitations under the License.
*/

#include <photon/net/http/server.h>
#include <photon/net/socket.h>
#include <photon/net/http/url.h>
#include <map>
#include <string>
#include <string_view>
#include "image_service.h"
#include "image_file.h"
#include "api_server.h"

int ApiHandler::handle_request(photon::net::http::Request& req,
photon::net::http::Response& resp,
std::string_view) {
auto target = req.target(); // string view, format: /snapshot?dev_id=${devID}&config=${config}
std::string_view query("");
auto pos = target.find('?');
if (pos != std::string_view::npos) {
query = target.substr(pos + 1);
}
LOG_DEBUG("Snapshot query: `", query); // string view, format: dev_id=${devID}&config=${config}
parse_params(query);
auto dev_id = params["dev_id"];
auto config_path = params["config"];
LOG_DEBUG("dev_id: `, config: `", dev_id, config_path);

int code;
std::string msg;
ImageFile* img_file = nullptr;

if (dev_id.empty() || config_path.empty()) {
code = 400;
msg = std::string(R"delimiter({
"success": false,
"message": "Missing dev_id or config in snapshot request"
class ApiHandler : public photon::net::http::HTTPHandler {
public:
ImageService *imgservice;
std::map<std::string, std::string> params;

ApiHandler(ImageService *imgservice) : imgservice(imgservice) {}
int handle_request(photon::net::http::Request& req,
photon::net::http::Response& resp,
std::string_view) override {
auto target = req.target(); // string view, format: /snapshot?dev_id=${devID}&config=${config}
std::string_view query("");
auto pos = target.find('?');
if (pos != std::string_view::npos) {
query = target.substr(pos + 1);
}
// auto query = req.query();
LOG_INFO("Snapshot query: `", query); // string view, format: dev_id=${devID}&config=${config}
parse_params(query);
auto dev_id = params["dev_id"];
auto config_path = params["config"];
LOG_DEBUG("dev_id: `, config: `", dev_id, config_path);

int code;
std::string msg;
ImageFile* img_file = nullptr;

if (dev_id.empty() || config_path.empty()) {
code = 400;
msg = std::string(R"delimiter({
"success": false,
"message": "Missing dev_id or config in snapshot request"
})delimiter");
goto EXIT;
}
goto EXIT;
}

img_file = imgservice->find_image_file(dev_id);
if (!img_file) {
code = 404;
msg = std::string(R"delimiter({
"success": false,
"message": "Image file not found"
img_file = imgservice->find_image_file(dev_id);
if (!img_file) {
code = 404;
msg = std::string(R"delimiter({
"success": false,
"message": "Image file not found"
})delimiter");
goto EXIT;
}
goto EXIT;
}

if (img_file->create_snapshot(config_path.c_str()) < 0) {
code = 500;
msg = std::string(R"delimiter({
"success": false,
"message": "Failed to create snapshot`"
if (img_file->create_snapshot(config_path.c_str()) < 0) {
code = 500;
msg = std::string(R"delimiter({
"success": false,
"message": "Failed to create snapshot`"
})delimiter");
goto EXIT;
}
goto EXIT;
}

code = 200;
msg = std::string(R"delimiter({
"success": true,
"message": "Snapshot created successfully"
code = 200;
msg = std::string(R"delimiter({
"success": true,
"message": "Snapshot created successfully"
})delimiter");

EXIT:
resp.set_result(code);
resp.headers.content_length(msg.size());
resp.keep_alive(true);
auto ret_w = resp.write((void*)msg.c_str(), msg.size());
if (ret_w != (ssize_t)msg.size()) {
LOG_ERRNO_RETURN(0, -1, "send body failed, target: `, `", req.target(), VALUE(ret_w));
resp.set_result(code);
resp.headers.content_length(msg.size());
resp.keep_alive(true);
auto ret_w = resp.write((void*)msg.c_str(), msg.size());
if (ret_w != (ssize_t)msg.size()) {
LOG_ERRNO_RETURN(0, -1, "send body failed, target: `, `", req.target(), VALUE(ret_w));
}
LOG_DEBUG("send body done");
return 0;
}
LOG_DEBUG("send body done");
return 0;
}

void ApiHandler::parse_params(std::string_view query) { // format: dev_id=${devID}&config=${config}...
if (query.empty())
return;

size_t start = 0;
while (start < query.length()) {
auto end = query.find('&', start);
if (end == std::string_view::npos) { // last one
end = query.length();
}
void parse_params(std::string_view query) { // format: dev_id=${devID}&config=${config}...
if (query.empty())
return;

auto param = query.substr(start, end - start);
auto eq_pos = param.find('=');
if (eq_pos != std::string_view::npos) {
auto key = param.substr(0, eq_pos);
auto value = param.substr(eq_pos + 1);
size_t start = 0;
while (start < query.length()) {
auto end = query.find('&', start);
if (end == std::string_view::npos) { // last one
end = query.length();
}

// url decode
auto decoded_key = photon::net::http::url_unescape(key);
auto decoded_value = photon::net::http::url_unescape(value);
params[decoded_key] = decoded_value;
} else {
// key without value
auto key = photon::net::http::url_unescape(param);
params[key] = "";
auto param = query.substr(start, end - start);
auto eq_pos = param.find('=');
if (eq_pos != std::string_view::npos) {
auto key = param.substr(0, eq_pos);
auto value = param.substr(eq_pos + 1);

// url decode
auto decoded_key = photon::net::http::url_unescape(key);
auto decoded_value = photon::net::http::url_unescape(value);
params[decoded_key] = decoded_value;
} else {
// key without value
auto key = photon::net::http::url_unescape(param);
params[key] = "";
}
start = end + 1;
}
start = end + 1;
}
}
};

struct ApiServer {
photon::net::ISocketServer* tcpserver = nullptr;
photon::net::http::HTTPServer* httpserver = nullptr;
ApiHandler* handler = nullptr;

ApiServer(ImageService *imgservice) : handler(new ApiHandler(imgservice)) {}

ApiServer::ApiServer(const std::string &addr, ApiHandler* handler) {
photon::net::http::URL url(addr);
std::string host = url.host().data(); // the string pointed by data() doesn't end up with '\0'
auto pos = host.find(":");
if (pos != host.npos) {
host.resize(pos);
~ApiServer() {
delete handler;
if(tcpserver) {
safe_delete(tcpserver);
}
if(httpserver) {
safe_delete(httpserver);
}
}
tcpserver = photon::net::new_tcp_socket_server();
tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1);
if(tcpserver->bind(url.port(), photon::net::IPAddr(host.c_str())) < 0)
LOG_ERRNO_RETURN(0, , "Failed to bind api server port `", url.port());
if(tcpserver->listen() < 0)
LOG_ERRNO_RETURN(0, , "Failed to listen api server port `", url.port());
httpserver = photon::net::http::new_http_server();
httpserver->add_handler(handler, false, "/snapshot");
tcpserver->set_handler(httpserver->get_connection_handler());
tcpserver->start_loop();
ready = true;
LOG_DEBUG("Api server listening on `:`, path: `", host, url.port(), "/snapshot");

int init(const std::string &addr) {
photon::net::http::URL url(addr);
std::string host = url.host().data(); // the string pointed by data() doesn't end up with '\0'
auto pos = host.find(":");
if (pos != host.npos) {
host.resize(pos);
}
tcpserver = photon::net::new_tcp_socket_server();
tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1);
if(tcpserver->bind(url.port(), photon::net::IPAddr(host.c_str())) < 0)
LOG_ERRNO_RETURN(0, -1, "Failed to bind api server port `", url.port());
if(tcpserver->listen() < 0)
LOG_ERRNO_RETURN(0, -1, "Failed to listen api server port `", url.port());
httpserver = photon::net::http::new_http_server();
httpserver->add_handler(handler, false, "/snapshot");
tcpserver->set_handler(httpserver->get_connection_handler());
tcpserver->start_loop();
LOG_DEBUG("Api server listening on `:`, path: `", host, url.port(), "/snapshot");
return 0;
}
};

int start_api_server(ApiServer *&api_server , ImageService *imgservice, const std::string &addr) {
api_server = new ApiServer(imgservice);
return api_server->init(addr);
}

ApiServer::~ApiServer() {
delete tcpserver;
delete httpserver;
}
int stop_api_server(ApiServer *api_server) {
safe_delete(api_server);
}
29 changes: 2 additions & 27 deletions src/api_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,5 @@

#pragma once

#include <map>
#include <string>
#include <photon/net/http/server.h>
#include <photon/net/socket.h>

class ImageService;

class ApiHandler : public photon::net::http::HTTPHandler {
public:
ImageService *imgservice;
std::map<std::string, std::string> params;

ApiHandler(ImageService *imgservice) : imgservice(imgservice) {}
int handle_request(photon::net::http::Request& req,
photon::net::http::Response& resp,
std::string_view) override;
void parse_params(std::string_view query);
};

struct ApiServer {
photon::net::ISocketServer* tcpserver = nullptr;
photon::net::http::HTTPServer* httpserver = nullptr;
bool ready = false;

ApiServer(const std::string &addr, ApiHandler* handler);
~ApiServer();
};
int start_api_server(ApiServer *&api_server , ImageService *imgservice, const std::string &addr);
int stop_api_server(ApiServer *api_server);
24 changes: 18 additions & 6 deletions src/image_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ void ImageFile::set_failed(const Ts &...xs) {
}
}

int ImageFile::create_snapshot(const char *config_path) {
int ImageFile::create_snapshot(const char *new_config_path) {
// load new config file to get the snapshot layer path
// open new upper layer
// restack() current RW layer as snapshot layer
Expand All @@ -565,13 +565,13 @@ int ImageFile::create_snapshot(const char *config_path) {
ImageConfigNS::ImageConfig new_cfg;
LSMT::IFileRW *upper_file = nullptr;

LOG_INFO("Load new config `.", config_path);
if (!new_cfg.ParseJSON(config_path)) {
LOG_ERROR_RETURN(0, -1, "Error parse new config json: `.", config_path);
LOG_INFO("Load new config `.", new_config_path);
if (!new_cfg.ParseJSON(new_config_path)) {
LOG_ERROR_RETURN(0, -1, "Error parse new config json: `.", new_config_path);
}

auto upper = new_cfg.upper();
auto lowers = new_cfg.lowers();
// auto lowers = new_cfg.lowers();
// if(lowers[lowers.size()-1].file() != conf.upper().data())
// LOG_ERROR_RETURN(0, -1, "The last lower layer(`) should be the same as old upper layer(`) after restack.", lowers[lowers.size()-1].file(), conf.upper().data());
if(upper.index() == conf.upper().index() || upper.data() == conf.upper().data())
Expand All @@ -589,7 +589,7 @@ int ImageFile::create_snapshot(const char *config_path) {
auto sealed = ((LSMT::IFileRW *)m_upper_file)->get_file(0);
((LSMT::IFileRO *)m_lower_file)->insert_file(sealed);
((LSMT::IFileRW *)m_upper_file)->clear_files();
delete m_upper_file;
safe_delete(m_upper_file);
}
// set m_lower_file->m_index = m_file->m_index->m_backing_index because m_file is not responsible for the destruction of m_backing_index
auto combo_index = (LSMT::IComboIndex *)((LSMT::IFileRW *)m_file)->index(); // m_file->m_index
Expand All @@ -599,6 +599,18 @@ int ImageFile::create_snapshot(const char *config_path) {
combo_index->front_index(upper_file_index);

m_upper_file = upper_file;

// overwrite the config file in use in case the old files are used again after the process restarts
auto lfs = photon::fs::new_localfs_adaptor();
if (lfs == nullptr) {
LOG_ERRNO_RETURN(0, -1, "new localfs_adaptor failed");
}
DEFER(delete lfs);
int ret = lfs->rename(new_config_path, this->config_path.c_str());
if (ret != 0) {
LOG_ERRNO_RETURN(0, -1, "rename(`,`) failed", new_config_path, this->config_path);
}
LOG_INFO("rename(`,`) success", new_config_path, this->config_path);

return 0;
}
7 changes: 4 additions & 3 deletions src/image_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ static std::string SEALED_FILE_NAME = "overlaybd.sealed";

class ImageFile : public photon::fs::ForwardFile {
public:
ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id)
: ForwardFile(nullptr), image_service(is), m_lower_file(nullptr) {
ImageFile(ImageConfigNS::ImageConfig &_conf, ImageService &is, const std::string &dev_id, const char *_config_path)
: ForwardFile(nullptr), image_service(is), m_lower_file(nullptr), config_path(_config_path) {
conf.CopyFrom(_conf, conf.GetAllocator());
m_exception = "";
if(image_service.register_image_file(dev_id, this) != 0) { // register itself
Expand Down Expand Up @@ -119,11 +119,12 @@ class ImageFile : public photon::fs::ForwardFile {

int compact(IFile *as);

int create_snapshot(const char *config_path);
int create_snapshot(const char *new_config_path);

private:
Prefetcher *m_prefetcher = nullptr;
ImageConfigNS::ImageConfig conf;
const std::string config_path;
std::list<BKDL::BkDownload *> dl_list;
photon::join_handle *dl_thread_jh = nullptr;
ImageService &image_service;
Expand Down
Loading
Loading