Stiffstream / restinio

Cross-platform, efficient, customizable, and robust asynchronous HTTP(S)/WebSocket server C++ library with the right balance between performance and ease of use
Other
1.15k stars 93 forks source link

Not sure if a proper bug: localhost sort-of broken? #94

Closed Lord-Kamina closed 4 years ago

Lord-Kamina commented 4 years ago

I'm trying to migrate Performous' webserver from CppRESTSDK; so far, I'm focusing on trying to get a basic RESTinio server working before fine-tuning everything and actually porting the router code.

Right now, a very basic server is working. However, if I set the address to my computer's local IP, I can only access it from that IP (neither localhost nor 127.0.0.1 will work). I am not sure whether this is an actual bug or whether I've configured something wrong. Care to help me please?

Also, two unrelated questions re: settings for which I couldn't find answers.

1) What's the unit-measure for the buffer_size; bytes? 2) I'm trying to understand exactly what read_next_http_message_timelimit is but I'm not sure I've got the right idea?

P.S. here's the code I'm using to open the server:

using Performous_Server_Traits = restinio::traits_t<
         restinio::asio_timer_manager_t,
         restinio::shared_performous_logger_t,
         Performous_Router_t,
         boost::asio::strand<boost::asio::executor>>;

using Performous_Server_Settings = restinio::run_on_thread_pool_settings_t<Performous_Server_Traits>;

Performous_Server_Settings RequestHandler::make_server_settings(const std::string &url, unsigned short port) {
    auto settings = Performous_Server_Settings(config["webserver/threads"].i());
    settings
    .port( port )
    .address( url )
    .separate_accept_and_create_connect(true)
    .request_handler(init_webserver_router())
    .read_next_http_message_timelimit(std::chrono::seconds(config["webserver/http_timelimit"].i()))
    .write_http_response_timelimit(std::chrono::seconds(config["webserver/write_http_timelimit"].i()))
    .handle_request_timeout(std::chrono::seconds(config["webserver/timeout"].i()))
    .buffer_size(std::size_t(config["webserver/buffer_size"].i()))
    .concurrent_accepts_count(config["webserver/threads"].i())
    .max_pipelined_requests(config["webserver/request_pipeline"].i());
    return settings;
}

std::unique_ptr<Performous_Router_t> RequestHandler::init_webserver_router() {
    auto router = std::make_unique<Performous_Router_t>();
    router->http_get( "/single/:param", []( auto req, auto params ){
        return
            init_resp( req->create_response() )
                .set_body(
                    fmt::format(
                        "GET request with single parameter: '{}'",
                        params[ "param" ] ) )
                .done();
    } );
    // GET request to homepage.
    router->http_get( "/", []( auto req, auto ){
        return
            init_resp( req->create_response() )
                .set_body( "GET request to the homepage.")
                .done();
    } );
    router->non_matched_request_handler(
            [](auto req){
                return req->create_response(restinio::status_not_found()).connection_close().done();
            });
    return router;
}

RequestHandler::RequestHandler(std::string url, unsigned short port, Songs& songs):m_listener("http://" + url), m_songs(songs), m_restinio_server(restinio::own_io_context(), make_server_settings(url, port)) {}

The RequestHandler class lives inside a WebServer class defined as follows:

class WebServer
{
public:
    WebServer(Songs& songs);
    ~WebServer();

private:
    void StartServer(int tried, bool fallbackPortInUse);
    std::string getIPaddr();    
    std::shared_ptr<std::thread> m_serverThread;
    std::shared_ptr<RequestHandler> m_server;
    Songs& m_songs;
};

WebServer::WebServer(Songs& songs)
: m_songs(songs)
{
    if(config["webserver/access"].i() == 0) {
        std::clog << "webserver/notice: Not starting webserver." << std::endl;
    } else {
        m_serverThread = std::make_unique<std::thread>([this] { StartServer(0, false); });
    }
}

And StartServer does the following (not the entire code but the relevant bits):

void WebServer::StartServer(int tried, bool fallbackPortInUse) {
    try {
        boost::asio::post(m_server->m_restinio_server.io_context(),
    [&] {
        // Starting the server in a sync way.
        m_server->m_restinio_server.open_sync();
    });
        m_server->m_restinio_server.io_context().run();
    } catch (std::exception& e) {
        std::clog << "webserver/error: Failed to open RESTinio server due to: " << e.what() << ". Trying again... (tried " << tried << " times.)" << std::endl;
        std::this_thread::sleep_for(20s);
        StartServer(tried, fallbackPortInUse);
    }
}

m_server in this case is the RequestHandler instance that contains our restinio::http_server_t

Lord-Kamina commented 4 years ago

Update: Starting the server using "localhost" makes it work only for localhost and not if I expressly use my local ip.

eao197 commented 4 years ago

However, if I set the address to my computer's local IP, I can only access it from that IP (neither localhost nor 127.0.0.1 will work).

Just use "0.0.0.0" as the address.

What's the unit-measure for the buffer_size; bytes?

Yes, bytes.

I'm trying to understand exactly what read_next_http_message_timelimit is but I'm not sure I've got the right idea?

It's the maximum duration between the start of reading a new http-request and receiving the whole request. For example, a client connects to the server and then sends a couple of bytes and stops sending anything and doesn't close the connection. That connection will be automatically closed by RESTinio after read_next_http_message_timelimit.

Lord-Kamina commented 4 years ago

However, if I set the address to my computer's local IP, I can only access it from that IP (neither localhost nor 127.0.0.1 will work).

Just use "0.0.0.0" as the address.

Perfect, thanks.

What's the unit-measure for the buffer_size; bytes?

Yes, bytes.

I'm trying to understand exactly what read_next_http_message_timelimit is but I'm not sure I've got the right idea?

It's the maximum duration between the start of reading a new http-request and receiving the whole request. For example, a client connects to the server and then sends a couple of bytes and stops sending anything and doesn't close the connection. That connection will be automatically closed by RESTinio after read_next_http_message_timelimit.

I see; I thought it might have to do with re-using a connection between one request and another.

eao197 commented 4 years ago

I thought it might have to do with re-using a connection between one request and another.

RESTinio is server only framework, there is no sense to re-use existing connection, it's a task of a client.

However, read_next_http_message_timelimit will be taken into account in the following case:

eao197 commented 4 years ago

I think this issue is resolved, so I close it. Feel free to reopen if you have some more questions.