Skip to content

Commit

Permalink
feat: restful server structure
Browse files Browse the repository at this point in the history
  • Loading branch information
neko-para committed Nov 10, 2023
1 parent b50366b commit 55beae9
Show file tree
Hide file tree
Showing 20 changed files with 585 additions and 6 deletions.
2 changes: 1 addition & 1 deletion 3rdparty/MaaDeps
Submodule MaaDeps updated 1 files
+4 −1 vcpkg.json
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ option(WITH_ADB_CONTROLLER "build with adb controller" ON)
option(WITH_THRIFT_CONTROLLER "build with thrift controller" ON)
option(WITH_DBG_CONTROLLER "build with debugging controller" ON)
option(WITH_GRPC "build with protobuf and grpc" ON)
option(WITH_RESTFUL "build with beast for restful server" ON)
option(BUILD_GRPC_CLI "build grpc CLI exec" ON)
option(BUILD_SAMPLE "build a demo" OFF)
option(BUILD_PIPELINE_TESTING "build pipeline testing" OFF)
Expand Down Expand Up @@ -54,6 +55,9 @@ if(WITH_GRPC)
include(${PROJECT_SOURCE_DIR}/cmake/grpc-gen.cmake)
add_compile_definitions(WITH_GRPC)
endif()
if (WITH_RESTFUL)
add_compile_definitions(WITH_RESTFUL)
endif()

find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
find_package(Boost REQUIRED COMPONENTS system)
Expand Down
8 changes: 6 additions & 2 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
add_subdirectory(MaaUtils)

if (WITH_ADB_CONTROLLER)
if(WITH_ADB_CONTROLLER)
add_subdirectory(MaaAdbControlUnit)
endif(WITH_ADB_CONTROLLER)
if(WITH_THRIFT_CONTROLLER AND NOT MAA_CROSSCOMPILE)
add_subdirectory(MaaThriftController)
endif(WITH_THRIFT_CONTROLLER AND NOT MAA_CROSSCOMPILE)
if (WITH_DBG_CONTROLLER)
if(WITH_DBG_CONTROLLER)
add_subdirectory(MaaDbgControlUnit)
endif(WITH_DBG_CONTROLLER)

Expand All @@ -17,4 +17,8 @@ if(WITH_GRPC)
add_subdirectory(MaaRpc)
endif(WITH_GRPC)

if(WITH_RESTFUL)
add_subdirectory(MaaRestful)
endif(WITH_RESTFUL)

add_subdirectory(binding)
18 changes: 18 additions & 0 deletions source/MaaRestful/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
file(GLOB_RECURSE maa_restful_src *.h *.hpp *.cpp)

add_executable(MaaRestful ${maa_restful_src})

target_include_directories(
MaaRestful
INTERFACE ../../include
PRIVATE . ../include ../../include)

target_link_libraries(MaaRestful MaaFramework MaaToolKit MaaUtils HeaderOnlyLibraries Boost::system)

install(
TARGETS MaaRestful
RUNTIME DESTINATION bin
LIBRARY DESTINATION bin
ARCHIVE DESTINATION lib)

source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${maa_restful_src})
37 changes: 37 additions & 0 deletions source/MaaRestful/Handle/Context.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include "Helper/Forward.hpp"

#include <meojson/json.hpp>

MAA_RESTFUL_NS_BEGIN

struct Context
{
Context(http::request<http::string_body>&& req, http::response<http::string_body>& res)
: req_(std::move(req)), res_(res)
{}

void json_body(const json::object& value)
{
res_.set(http::field::content_type, "application/json");
res_.body() = value.to_string();
}

void bad_request(const std::string& why)
{
res_.result(http::status::bad_request);
json_body({ { "error", why } });
}

void init()
{
res_.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res_.keep_alive(req_.keep_alive());
}

http::request<http::string_body> req_;
http::response<http::string_body>& res_;
};

MAA_RESTFUL_NS_END
71 changes: 71 additions & 0 deletions source/MaaRestful/Handle/DeviceDispatcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "Handle/DeviceDispatcher.hpp"
#include "MaaToolKit/MaaToolKitAPI.h"
#include "Utils/Format.hpp"

MAA_RESTFUL_NS_BEGIN

bool DeviceDispatcher::handle(Context& ctx, std::vector<std::string_view> url_segs)
{
auto seg0 = url_segs[0];

if (seg0 == "devices") {
if (url_segs.size() == 1) {
// switch (ctx.req_.method()) {
// case http::verb::get:
// // TODO: return all devices
// ctx.json_body({ { "count", MaaToolKitFindDevice() } });
// break;
// default:
// ctx.bad_request(MAA_FMT::format("bad verb {}", std::string_view(ctx.req_.method_string())));
// }
// return true;
}
else {
auto seg1 = url_segs[1];

if (seg1 == "find") {
switch (ctx.req_.method()) {
case http::verb::put:
ctx.json_body({ { "count", MaaToolKitFindDevice() } });
break;
default:
ctx.bad_request(MAA_FMT::format("bad verb {}", std::string_view(ctx.req_.method_string())));
}
return true;
}
}
}
else if (seg0 == "device") {
if (url_segs.size() == 1) {
ctx.bad_request(MAA_FMT::format("id expected"));
return true;
}
else {
auto seg1 = url_segs[1];

int id = std::stoul(std::string(seg1));

if (url_segs.size() == 2) {
switch (ctx.req_.method()) {
case http::verb::get:
ctx.json_body({ { "name", MaaToolKitGetDeviceName(id) },
{ "adb_path", MaaToolKitGetDeviceAdbPath(id) },
{ "adb_serial", MaaToolKitGetDeviceAdbSerial(id) },
{ "controller_type", MaaToolKitGetDeviceAdbControllerType(id) },
{ "adb_config", MaaToolKitGetDeviceAdbConfig(id) } });
break;
default:
ctx.bad_request(MAA_FMT::format("bad verb {}", std::string_view(ctx.req_.method_string())));
}
return true;
}
else {
// TODO: implement partial query
}
}
}

return false;
}

MAA_RESTFUL_NS_END
12 changes: 12 additions & 0 deletions source/MaaRestful/Handle/DeviceDispatcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "Handle/Dispatcher.hpp"

MAA_RESTFUL_NS_BEGIN

struct DeviceDispatcher : public Dispatcher<void>
{
virtual bool handle(Context& ctx, std::vector<std::string_view> url_segs) override;
};

MAA_RESTFUL_NS_END
80 changes: 80 additions & 0 deletions source/MaaRestful/Handle/Dispatcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "Handle/Dispatcher.hpp"
#include "Handle/RootDispatcher.hpp"
#include "MaaFramework/MaaAPI.h"
#include "Utils/Format.hpp"
#include <iostream>
#include <ranges>

MAA_RESTFUL_NS_BEGIN

http::message_generator handle_request(http::request<http::string_body>&& req)
{
http::response<http::string_body> res { http::status::ok, req.version() };

std::string url = req.target();
auto url_segs_rng = std::string_view(url) | std::views::split('/');
std::vector<std::string_view> url_segs;
for (auto seg : url_segs_rng) {
std::string_view part(seg.begin(), seg.end());
if (part.length() == 0) {
continue;
}
url_segs.push_back(part);
}

Context ctx(std::move(req), res);
ctx.init();

RootDispatcher rd;
if (!rd.handle(ctx, url_segs)) {
ctx.bad_request(MAA_FMT::format("unknown path {}", std::string_view(ctx.req_.target())));
}

res.prepare_payload();

return res;

// auto const bad_request = [&req](beast::string_view why) {
// http::response<http::string_body> res { http::status::bad_request, req.version() };
// res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
// res.set(http::field::content_type, "text/html");
// res.keep_alive(req.keep_alive());
// res.body() = std::string(why);
// res.prepare_payload();
// return res;
// };

// // auto const not_found = [&req](beast::string_view target) {
// // http::response<http::string_body> res { http::status::not_found, req.version() };
// // res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
// // res.set(http::field::content_type, "text/html");
// // res.keep_alive(req.keep_alive());
// // res.body() = "The resource '" + std::string(target) + "' was not found.";
// // res.prepare_payload();
// // return res;
// // };

// // auto const server_error = [&req](beast::string_view what) {
// // http::response<http::string_body> res { http::status::internal_server_error, req.version() };
// // res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
// // res.set(http::field::content_type, "text/html");
// // res.keep_alive(req.keep_alive());
// // res.body() = "An error occurred: '" + std::string(what) + "'";
// // res.prepare_payload();
// // return res;
// // };

// if (req.method() != http::verb::get) {
// return bad_request("Unknown HTTP-method");
// }

// http::response<http::string_body> res { http::status::ok, req.version() };
// res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
// res.set(http::field::content_type, "text/plain");
// res.body() = req.target();
// res.prepare_payload();
// res.keep_alive(req.keep_alive());
// return res;
}

MAA_RESTFUL_NS_END
33 changes: 33 additions & 0 deletions source/MaaRestful/Handle/Dispatcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "Handle/Context.hpp"

#include <string>
#include <vector>

MAA_RESTFUL_NS_BEGIN

struct DispatcherBase
{
virtual ~DispatcherBase() {}
};

template <typename Env>
struct Dispatcher
{
Dispatcher(Env env) : env_(env) {}

virtual bool handle(Context& ctx, std::vector<std::string_view> url_segs) = 0;

Env env_;
};

template <>
struct Dispatcher<void>
{
virtual bool handle(Context& ctx, std::vector<std::string_view> url_segs) = 0;
};

http::message_generator handle_request(http::request<http::string_body>&& req);

MAA_RESTFUL_NS_END
37 changes: 37 additions & 0 deletions source/MaaRestful/Handle/RootDispatcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "Handle/RootDispatcher.hpp"
#include "MaaFramework/MaaAPI.h"
#include "Utils/Format.hpp"

MAA_RESTFUL_NS_BEGIN

bool RootDispatcher::handle(Context& ctx, std::vector<std::string_view> url_segs)
{
if (url_segs.size() == 0) {
switch (ctx.req_.method()) {
case http::verb::get:
ctx.json_body({ { "msg", "hello world!" } });
break;
default:
ctx.bad_request(MAA_FMT::format("bad verb {}", std::string_view(ctx.req_.method_string())));
}
return true;
}
else {
auto seg0 = url_segs[0];

if (seg0 == "version") {
switch (ctx.req_.method()) {
case http::verb::get:
ctx.json_body({ { "version", MaaVersion() } });
break;
default:
ctx.bad_request(MAA_FMT::format("bad verb {}", std::string_view(ctx.req_.method_string())));
}
return true;
}

return device_.handle(ctx, url_segs);
}
}

MAA_RESTFUL_NS_END
14 changes: 14 additions & 0 deletions source/MaaRestful/Handle/RootDispatcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include "Handle/DeviceDispatcher.hpp"

MAA_RESTFUL_NS_BEGIN

struct RootDispatcher : public Dispatcher<void>
{
virtual bool handle(Context& ctx, std::vector<std::string_view> url_segs) override;

DeviceDispatcher device_;
};

MAA_RESTFUL_NS_END
9 changes: 9 additions & 0 deletions source/MaaRestful/Helper/Forward.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include "Conf/Conf.h"
#include "Utils/Boost.hpp"

namespace beast = boost::beast;
namespace http = beast::http;
namespace asio = boost::asio;
using tcp = asio::ip::tcp;
13 changes: 13 additions & 0 deletions source/MaaRestful/Helper/Log.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "Helper/Forward.hpp"
#include "Utils/Logger.h"

MAA_RESTFUL_NS_BEGIN

inline void fail(beast::error_code ec, char const* what)
{
LogError << what << ec.message();
}

MAA_RESTFUL_NS_END
Loading

0 comments on commit 55beae9

Please sign in to comment.