etr/libhttpserver

Is it possible to start the server in a newly detached thread?

ariestav opened this issue · 2 comments

I'm developing a library to be shipped as a .bundle on macOS and as a .dll for Windows. This library is loaded into a host application during its runtime, and my objective is to start a local web server from this library when it loads.

There is an initialization function that is called when this library is loaded by the host app. I placed the code to create the web server in that init function. It looks something like:

/* Start local http server */
webserver ws =  create_webserver()
                .port(1337)
                .start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION);

hello_world_resource hwr;
ws.register_resource("/hello", &hwr);
ws.start(true);

When debugging, I do find that the library gets loaded by the host app because when I point a browser to http://localhost:1337/hello it shows the rendered "Hello World!" HTML page from the example. However, it seems like the listener is blocking the host app's main thread because it locks up the host app because it just hangs after the server starts. I thought by using the THREAD_PER_CONNECTION value in the .start_method() function that it wouldn't do this, but it's not the case.

I also tried ws.start(false) and in this case the host app's main thread is not blocked, but then the server does not appear to be running at all — the "Hello World" page does return in this case and the server just doesn't exist.

So, my question is if it's possible to start the web server process on a detached thread so that it both runs, but does NOT block the main thread? If so, how can it be done?

Many thanks!

etr commented

Hey, if you leave the ws.start(true) it will block the execution and the thread in waiting. Passing it as false, just makes it not blocking but that also means that something else must keep the scope alive otherwise the webserver is stopped and destroyed when it goes out of scope.

There are two ways to start the server so it doesn't block your main thread. One is to run it through ws.start(false) but initialize the object in a scope that doesn't get destroyed (or instantiate it in heap). The other, usually simpler, is to create the webserver and run it from within a separate thread keeping it blocking configuration.

To do the second option, you basically can just put your initializing code within a function and call such function through std::thread. Again though, whatever scope has initialized the thread needs not to close.

If you can share some of the code of the host app, it might help me to give you more precise guidance.

Thank you for the details and suggestion.

I couldn't find a place where the scope of ws would remain in the context of the host app. So, I moved onto the separate thread. Here is what I ended up using.

At the top of my code, in the global scope I used:

#include <httpserver.hpp>
using namespace httpserver;

//Responses from server.
class hello_world_resource : public http_resource {
public:
    const std::shared_ptr<http_response> render(const http_request&) {
        return std::shared_ptr<http_response>(new string_response("Hello, World!"));
    }
};

int startHTTPServer () {
    
    hello_world_resource hwr;
    webserver ws = create_webserver(8080);
    
    ws.register_resource("/hello", &hwr);
    ws.start(true);
    
    return 0;
    
}

Then, in the initialization function of the lib that I am developing, I created a thread with the example code, but then I had to detach the thread so that it would not block the host app's main thread:

...

thread webserver(startHTTPServer);   
webserver.detach();  //detaching was the key to making it work

...

Thank you for your suggestions and for this awesome library!