1

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!

Biggytiny
  • 519
  • 10
  • 29

4 Answers4

3

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;
}
Biggytiny
  • 519
  • 10
  • 29
  • Note that 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. – Chih-Chen Kao Oct 02 '20 at 20:23
2

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;
}

0

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.

mahee96
  • 705
  • 6
  • 15
0

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..";
}

codeworks
  • 149
  • 1
  • 15