0xdead4ead/BeastHttp

turn on SO_REUSEADDR for listening socket (always, preferably)

saurik opened this issue · 3 comments

acceptor_.set_option(boost::asio::socket_base::reuse_address(false));

So, I am used to webservers always having SO_REUSEADDR, but right now BeastHttp is going out of its way to disable this feature. If nothing else, you have the problem that if the server dies for some reason, you are unable to start it again until all of the sockets it previously had exit the TIME_WAIT state, which is pure devastating downtime (you just can't start the server due to "system:48 bind/loop").

I would think if this value is to be hardcoded it should hardcoded to true, not false. FWIW, Apache firmly turns this on for all platforms other than Windows (where the behavior of the flag is apparently both different and unnecessary? so not turning it on on Windows is equivalent to turning it on on Linux, but turning it on on Windows is more like SO_REUSEPORT; see warmcat/libwebsockets#65).

static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
{
apr_socket_t *s = server->sd;
int one = 1;
...

#ifndef WIN32
    stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
    if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
        ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00067)
                      "make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
                      server->bind_addr);
        apr_socket_close(s);
        return stat;
    }
#endif

(Frankly, I'd think that Boost should fix that upstream, though, with reuse_address being a no-op on Windows and binding it under some different name. FWIW, if you want to do a special case in this code, I'd highly recommend having the code turn SO_EXCLUSIVEADDRUSE on on Windows, to prevent the ability to use the Windows SO_REUSEADDR to hijack the port from a running instance.)

https://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t

Here is a list of links to the code from various web servers showing they all turn this feature on, as well as a citation from Unix Network Programming, which asserts "all TCP servers should specify this socket option to allow the server to be restarted in this situation" (though the context in the original book is subtly different--a server that implements post-fork--it still applies as you might fork).

https://stackoverflow.com/questions/6960219/why-not-using-so-reuseaddr-on-unix-tcp-ip-servers

Essentially, I think my recommendation is currently to do this (which I've verified to compile on clang+MinGW).

#ifdef _WIN32
    typedef boost::asio::detail::socket_option::boolean<BOOST_ASIO_OS_DEF(SOL_SOCKET), SO_EXCLUSIVEADDRUSE> exclusive_address_use;
    acceptor_.set_option(exclusive_address_use(true));
#else
    acceptor_.set_option(boost::asio::socket_base::reuse_address(true));
#endif

I do not remember why I set this option, but I assume that this way I wanted to stop the application if this transport protocol port is already in use. This is confirmed by the presence of code in the examples.

// I/O context will be stopped, if code value is EADDRINUSE or EACCES
        if (code == boost::system::errc::address_in_use or
                code == boost::system::errc::permission_denied)
            ioc.stop();

This makes server debugging difficult and everything you say above is true!
I do not mind to introduce your changes. I do not know the network architecture of Windows platform. Thanks for a help!

Will apply fix in next commit!