pistacheio / pistache

A high-performance REST toolkit written in C++
https://pistacheio.github.io/pistache/
Apache License 2.0
3.18k stars 698 forks source link

pistache server URL is not reatcheable after remove and add route #1103

Closed chorfa007 closed 1 year ago

chorfa007 commented 1 year ago

Dear All,

I would like to implement a server where i add and remove routes dynamically without rebooting server. Below the example

`#pragma once
#include <pistache/endpoint.h>
#include <pistache/http.h>
#include <pistache/router.h>

#include "rapidjson/document.h"
#include <csignal>
#include <iostream>

using namespace Pistache;
using namespace Rest;
class Server
{
public:
    enum Modes
    {
        mode_1,
        mode_2
    };    
    Server();
    virtual ~Server() = default;
    void SwitchMode();
    void StartServer(Server::Modes mode);
    void StopServer();

private:
    Router router_;
    Router empty_router_;
    std::shared_ptr<Http::Endpoint> endpoint_;
private:
    void SetupRoutes();
    void hello_fun_1_mode_1(const Rest::Request& request, Http::ResponseWriter response);
    void hello_fun_2_mode_1(const Rest::Request& request, Http::ResponseWriter response);
    void hello_fun_1_mode_2(const Rest::Request& request, Http::ResponseWriter response);
    void hello_fun_2_mode_2(const Rest::Request& request, Http::ResponseWriter response);
    Modes system_mode_ = Modes::mode_2;
};
#include "Server.hpp"

using namespace Pistache;
using namespace Rest;
Server::Server()
{
    Port port(9900);
    Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(port));
    endpoint_ = std::make_unique<Pistache::Http::Endpoint>(addr);
    auto opts = Pistache::Http::Endpoint::options()
            .threads(2)
            .flags(Pistache::Tcp::Options::ReuseAddr);
    endpoint_->init(opts);
}

void Server::StartServer(Server::Modes mode)
{
    system_mode_ = mode;
    SetupRoutes();
    endpoint_->setHandler(router_.handler());
    endpoint_->serveThreaded();
}

void Server::StopServer()
{
    endpoint_->shutdown();
}

void Server::SetupRoutes()
{
    using namespace Pistache::Rest;
    if (system_mode_ == Modes::mode_1)
    {
        Routes::Get(router_, "/hello_fun_1_mode_1", Routes::bind(&Server::hello_fun_1_mode_1, this));
        Routes::Get(router_, "/hello_fun_2_mode_1", Routes::bind(&Server::hello_fun_2_mode_1, this));
    }
    else
    {
        Routes::Get(router_, "/hello_fun_1_mode_2", Routes::bind(&Server::hello_fun_2_mode_2, this));
        Routes::Get(router_, "/hello_fun_2_mode_2", Routes::bind(&Server::hello_fun_2_mode_2, this));
    }
}

void Server::SwitchMode()
{
    if (system_mode_ == Modes::mode_1)
    {
        std::cout<<"switch to mode_2 mode";
        Routes::Remove(router_, Pistache::Http::Method::Get, "/hello_fun_1_mode_1");
        Routes::Remove(router_, Pistache::Http::Method::Get, "/hello_fun_2_mode_1");
        system_mode_ = Modes::mode_2;
        SetupRoutes();
    }
    else
    {
        std::cout<<"switch to mode_1 mode";
        Routes::Remove(router_, Pistache::Http::Method::Get, "/hello_fun_1_mode_2");
        Routes::Remove(router_, Pistache::Http::Method::Get, "/hello_fun_1_mode_2");
        system_mode_ = Modes::mode_1;
        SetupRoutes();
    }
}

void Server::hello_fun_1_mode_1(const Rest::Request& request, Http::ResponseWriter response)
{
    response.send(Http::Code::Ok, "hello_fun_1_mode_1 !");
}
void Server::hello_fun_2_mode_1(const Rest::Request& request, Http::ResponseWriter response)
{
    response.send(Http::Code::Ok, "hello_fun_2_mode_1 !");
}
void Server::hello_fun_1_mode_2(const Rest::Request& request, Http::ResponseWriter response)
{
    response.send(Http::Code::Ok, "hello_fun_1_mode_2 !");
}
void Server::hello_fun_2_mode_2(const Rest::Request& request, Http::ResponseWriter response)
{
    response.send(Http::Code::Ok, "hello_fun_2_mode_2 !");
}`

Each time i call the function Routes::Remove via Server::SwitchMode(), i end with exception : terminate called after throwing an instance of 'std::runtime_error' what(): Requested does not exist. Aborted (core dumped) Below the back trace :+1: `(gdb) bt

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7aa2859 in __GI_abort () at abort.c:79
#2  0x00007ffff7d2b911 in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7d3738c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7d373f7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7d376a9 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff7efb22e in Pistache::Rest::SegmentTreeNode::removeRoute (this=0x555555574bb0, path=...) at ../src/server/router.cc:233
#7  0x00007ffff7f6b470 in Pistache::Rest::SegmentTreeNode::removeRoute (this=0x5555555748b0, path=...) at /usr/include/c++/9/bits/shared_ptr_base.h:1020
#8  0x00007ffff7f6c928 in Pistache::Rest::Router::removeRoute (this=<optimized out>, method=<optimized out>, resource="/hello_fun_1_mode_2") at ../src/server/router.cc:452
#9  0x00005555555591cb in Server::SwitchMode (this=0x5555555744f0) at /usr/include/c++/9/bits/char_traits.h:300
#10 0x00005555555579d9 in main (argc=<optimized out>, argv=<optimized out>) at /usr/include/c++/9/bits/unique_ptr.h:360
`

The problem is in line 233 file src/server/router.cc. I would like to know if i' m using the correct syntax Routes::remove ? If no then i guess this can be a possible issue in the pistache server and need to be fixed.

Thanks.

dennisjenkins75 commented 1 year ago

Please edit the above comment to fix the line-wrap on the stack trace.

I agree that being able to edit the routes live would be a really nice feature. If the API is there, then it should work.

The most useful thing to do would be to create a PR (pull request) that adds a (currently crashing) unit test that replicates the above. Someone else can then get your PR onto their development box and dig into the problem.

chorfa007 commented 1 year ago

All, I went deeper into my example and was able to resolve the exception, the root cause was that I deleted a route that does not exist:( I added extra checks and i have seen that if i start the server with :

  1. URL 1 mode 1
  2. URL 2 mode 1 Then i perform
  3. REMOVE URL 1 mode 1
  4. REMOVE URL 2 mode 1
  5. Add URL1 mode 2
  6. Add URL1 mode 2

Here i'm excepted that both URL 1 and 2 mode 2 should be reachable which is not the case, so i believe that the issue is here. The API cannot rebuild the data structure of routes correctly after a dynamisc changes . [It is not taking into account the new route, it is look like the data structure holding routes in the library is cleared and never updataed after calling route::remove) You can replicate the same with my pull request https://github.com/pistacheio/pistache/pull/1105/commits/aae7a4a1b55ee54d93eef167a3f321455af8a4da

Please note that the Router::remove API is not tested as a unit, which may explain why this issue was not detected previously.

All thanks you in advance for your assistance.

chorfa007 commented 1 year ago

To make the route mutable please use ` /**

iso std::shared_ptr<Private::RouterHandler> handler() const;

Please note that the API is not thread safe, you can refer the example of arghness if need a thread safe API https://github.com/pistacheio/pistache/pull/1105