diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 313288e..6025ff9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: lint: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install dependencies run: sudo apt install -y clang-format @@ -25,15 +25,15 @@ jobs: container: archlinux:latest steps: - name: Install dependencies - run: pacman -Syu --noconfirm base-devel clang cmake ninja extra-cmake-modules fmt libuv boost lsof + run: pacman -Syu --noconfirm base-devel clang cmake ninja extra-cmake-modules libuv boost lsof - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: repository: fcitx/fcitx5 path: fcitx5 - name: Cache fcitx5 data files - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: 'fcitx5/**/*.tar.*' key: ${{ runner.os }}-${{ hashFiles('fcitx5/src/modules/spell/CMakeLists.txt')}} @@ -46,7 +46,7 @@ jobs: -DENABLE_KEYBOARD=Off -DENABLE_X11=Off -DENABLE_WAYLAND=Off -DENABLE_ENCHANT=Off -DENABLE_DBUS=Off -DENABLE_SERVER=Off -DENABLE_EMOJI=Off -DUSE_SYSTEMD=Off -DENABLE_TEST=OFF - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: path: fcitx5-beast diff --git a/src/beast.cpp b/src/beast.cpp index f714dcf..6b1672e 100644 --- a/src/beast.cpp +++ b/src/beast.cpp @@ -10,6 +10,8 @@ #include #endif +#define REMOTE_PREFIX "/remote/" + namespace beast = boost::beast; namespace http = beast::http; namespace net = boost::asio; @@ -19,6 +21,10 @@ namespace fcitx { ConfigSetter configSetter_ = [](const char *, const char *) {}; ConfigGetter configGetter_ = [](const char *) { return ""; }; +RemoteHandler remoteHandler_ = + [](const std::string_view, const char *) -> std::pair { + return {false, ""}; +}; Beast::Beast(Instance *instance) : instance_(instance) { reloadConfig(); } @@ -109,7 +115,7 @@ class http_connection // Construct a response message based on the program state. void create_response() { - if (stringutils::startsWith(request_.target(), "/config/")) { + if (request_.target().starts_with("/config/")) { std::string uri = "fcitx:/"; uri += request_.target(); if (request_.method() == http::verb::get) { @@ -122,6 +128,15 @@ class http_connection beast::ostream(response_.body()) << ""; configSetter_(uri.c_str(), request_.body().data()); } + } else if (request_.target().starts_with(REMOTE_PREFIX) && + request_.method() == http::verb::post) { + std::string_view command = request_.target(); + stringutils::consumePrefix(command, REMOTE_PREFIX); + auto res = remoteHandler_(command, request_.body().data()); + response_.set(http::field::content_type, "text/plain"); + response_.result(res.first ? http::status::ok + : http::status::bad_request); + beast::ostream(response_.body()) << res.second; } else { response_.result(http::status::not_found); response_.set(http::field::content_type, "text/plain"); diff --git a/src/beast.h b/src/beast.h index 43f3fcf..d9d3b40 100644 --- a/src/beast.h +++ b/src/beast.h @@ -21,6 +21,8 @@ namespace asio = boost::asio; using ConfigGetter = std::function; using ConfigSetter = std::function; +using RemoteHandler = std::function( + const std::string_view, const char *)>; namespace fcitx { @@ -58,6 +60,7 @@ FCITX_CONFIGURATION(BeastConfig, extern ConfigGetter configGetter_; extern ConfigSetter configSetter_; +extern RemoteHandler remoteHandler_; class Beast : public AddonInstance { public: @@ -72,6 +75,7 @@ class Beast : public AddonInstance { void reloadConfig() override; void setConfigGetter(ConfigGetter getter) { configGetter_ = getter; } void setConfigSetter(ConfigSetter setter) { configSetter_ = setter; } + void setRemoteHandler(RemoteHandler handler) { remoteHandler_ = handler; } private: static const inline std::string ConfPath = "conf/beast.conf"; diff --git a/test/testbeast.cpp b/test/testbeast.cpp index 7b1306d..c0f59b4 100644 --- a/test/testbeast.cpp +++ b/test/testbeast.cpp @@ -28,6 +28,7 @@ int main() { bool getterCalled = false; bool setterCalled = false; + bool remoteCalled = false; beast->setConfigGetter([&](const char *) { getterCalled = true; @@ -37,6 +38,11 @@ int main() { setterCalled = true; return ""; }); + beast->setRemoteHandler([&](const std::string_view, + const char *) -> std::pair { + remoteCalled = true; + return {true, ""}; + }); // Listen on default unix socket. assert(std::system("lsof /tmp/fcitx5.sock") == 0); @@ -53,6 +59,12 @@ int main() { "http://fcitx/config/addon/beast") == 0); assert(setterCalled); + // Remote works. + assert(!remoteCalled); + assert(std::system("curl --unix-socket /tmp/fcitx5.sock -X POST " + "http://fcitx/remote/c") == 0); + assert(remoteCalled); + // Unix socket path reset works. auto config = dynamic_cast(beast->getConfig()); RawConfig raw; @@ -73,6 +85,7 @@ int main() { getterCalled = false; setterCalled = false; + remoteCalled = false; // Getter works over TCP. assert(!getterCalled); @@ -80,12 +93,18 @@ int main() { "http://127.0.0.1:32489/config/addon/beast") == 0); assert(getterCalled); - // Setter works ovet TCP. + // Setter works over TCP. assert(!setterCalled); assert(std::system("curl -X POST -d '{}' " "http://127.0.0.1:32489/config/addon/beast") == 0); assert(setterCalled); + // Remote works over TCP. + assert(!remoteCalled); + assert(std::system("curl -X POST " + "http://127.0.0.1:32489/remote/c") == 0); + assert(remoteCalled); + // Tcp port reset works. cfg.tcp.mutableValue()->port.setValue(32490); cfg.save(raw);