简体   繁体   中英

How to keep server running (cpprestsdk - casablanca)

I am developing a REST api using Microsoft's cpprestsdk (aka casablanca) and I am having trouble keeping the server running when executing my code.

See my main.cpp here:

int main() {
    cout << "Starting Server" << endl;

    TransactionController server;
    server.setEndpoint("http://0.0.0.0:4200/api");
    server.initHandlers();

    try {
        server.openServer();
        cout << "Server listening at: " << server.getEndpoint() << endl;

        // figure out how to keep server running without this?
        while (true);
    }
    catch(exception &e) {
        cout << "--- ERROR DETECTED ---" << endl;
        cout << e.what() << endl;
    }


    // this doesn't get reached bc of the while(true)
    server.closeServer();

    return 0;
}

Additionally, for reference this is the implementation or the functions I have in my main.cpp:

pplx::task<void> TransactionController::openServer() {
    return listener.open();
}

pplx::task<void> TransactionController::closeServer() {
    return listener.close();
}

std::string TransactionController::getEndpoint() const{
    return listener.uri().to_string();
}


void TransactionController::initHandlers() {
    listener.support(methods::GET, bind(&TransactionController::handleGet, this, std::placeholders::_1));
    listener.support(methods::POST, bind(&TransactionController::handlePost, this, placeholders::_1));
}

void TransactionController::setEndpoint(const string& value) {
    listener = http_listener(value);
}

I have found a not ideal workaround of adding a

while(true);

to keep the server running until I manually halt execution.

I would like to implement this feature in a more elegant way however. I have explored the documentation online but have not been able to find the right methodology.

Any tips or pointers in the right direction would be greatly appreciated, as I have never worked with Casablanca before. Thank you for your time!

So I managed to figure it out by using code provided here:

https://github.com/ivanmejiarocha/micro-service/blob/master/source/foundation/include/usr_interrupt_handler.hpp

Here is now my new main.cpp:

int main() {
    cout << "Starting Server" << endl;

    InterruptHandler::hookSIGINT();

    TransactionController server;
    server.setEndpoint("http://0.0.0.0:4200/api");
    server.initHandlers();

    try {
        server.openServer().wait();
        cout << "Server listening at: " << server.getEndpoint() << endl;

        InterruptHandler::waitForUserInterrupt();

        server.closeServer().wait();
        cout << "Shutting Down Server" << endl;
    }
    catch(exception &e) {
        cout << "--- ERROR DETECTED ---" << endl;
        cout << e.what() << endl;
    }    

    return 0;
}

Using std::signal with signal handler as denoted in the accepted anwser is not really an option here since according to the C++ reference:

signal handlers are expected to have C linkage and, in general, only use the features from the common subset of C and C++. It is implementation-defined if a function with C++ linkage can be used as a signal handler.

And yet, notify_{one,all} is not signal safe, and thus cannot be used within a signal handler.

Alternatively, since cpprestsdk depends on boost asio, one can leverage the io_context for signal handling. The code snippet below demonstrates the concept:

void handle_get(web::http::http_request request) {
  //...
}

void interrupt_handler( const boost::system::error_code& error , int signal_number ) {
  //...
}

int main() {

  using web::http::experimental::listener::http_listener;
  auto url = web::http::uri("http://127.0.0.1:8051");

  http_listener listener(url);
  listener.support(web::http::methods::GET, handle_get);
  listener.open().then([]() { std::cout << "listening ..." << std::endl; }).wait();

  boost::asio::io_context handler_context;
  boost::asio::signal_set signals(handler_context, SIGINT );

  signals.async_wait( interrupt_handler );
  handler_context.run();

  return 0;
}

One way could be to delegate the start call to a thread and by adding a flag (eg isClosed ) for your while loop for signal to close (Check std::atomic for this). And, your close call will set isClosed to true and the thread will gracefully exit. Don't forget to call thread's join in your class destructor.

Here's an example of your callback for server thread:

void YourClass::startThreadCallback()
{
    // Start listener here...

    while ( !isClosed )
    {
        std::this_thread::sleep_for( 1ms );
    }
}

The way I did it was by using a std::getline in a do-while loop which will terminate the server only via proper command. I found this to be a quick fix which is less resource hungry.

    http_listener listener(L"http://localhost:80");

    try {
        listener
            .open()
            .then([&listener]() {std::wcout << L"\nstarting to listen\n"; })
            .wait();
    } catch(exception const& e){
        std::wcout << e.what() << std::endl;
    }

    std::string choice= "";
    do {
        std::getline(cin, choice);
    } while (!(choice == "N" || choice == "n"));
    return 0;

Note: plain std::cin will handle a string input as distinct chars and the loop will be executed until the length of the string, work around is to ignore rest of the chars but I decided to make a good use of the input (keyword to terminate server)

Other ways would be to set a semaphore wait in the main and releasing it via required signal while handling the HTTP requests in the appropriate methods(GET/PUT/POST/DELETE) for server termination.

If your http_listener is a pointer, you can avoid the infinite loop. Create an object of http_listener and wait and you should be able to remove the infinite loop and the program continues.

The following code works,

void initialize() {
    http_listener *listener;
    listener = new http_listener(L"http://127.0.0.1:1444/vessel");
    listener->support(methods::GET, get_vessel_visitpositions);

    try
    {
        listener->
            open()
            .then([&listener](){})
            .wait();

        //while (true);
    }
    catch (exception const & e)
    {
        wcout << e.what() << endl;
        return;
    }

    cout<<"Listening started..";
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM