Add a socket option to apr to control to TCP keepalive idle time. It is named APR_TCP_KEEPALIVE_IDLE. Index: apr-1.4.8/include/apr.hw =================================================================== --- apr-1.4.8.orig/include/apr.hw +++ apr-1.4.8/include/apr.hw @@ -105,6 +105,7 @@ #ifndef _WIN32_WCE #include #include +#include #include #else #include Index: apr-1.4.8/include/apr_network_io.h =================================================================== --- apr-1.4.8.orig/include/apr_network_io.h +++ apr-1.4.8/include/apr_network_io.h @@ -99,6 +99,11 @@ extern "C" { * until data is available. * @see apr_socket_accept_filter */ +#define APR_TCP_KEEPALIVE_IDLE 131072 /**< Sets the idle time (secs) to start + * sending TCP keep-alives. If 0 + * APR_SO_KEEPALIVE is implicitely enabled, + * otherwise implicitely enabled. The interval + * time is always set to 1 second. */ /** @} */ Index: apr-1.4.8/include/arch/win32/apr_arch_misc.h =================================================================== --- apr-1.4.8.orig/include/arch/win32/apr_arch_misc.h +++ apr-1.4.8/include/arch/win32/apr_arch_misc.h @@ -472,6 +472,22 @@ APR_DECLARE_LATE_DLL_FUNC(DLL_WINSOCK2AP #define WSAPoll apr_winapi_WSAPoll #define HAVE_POLL 1 +#ifdef WSAIoctl +#undef WSAIoctl +#endif +APR_DECLARE_LATE_DLL_FUNC(DLL_WINSOCK2API, int, WSAAPI, WSAIoctl, 0, ( + IN SOCKET s, + IN DWORD dwIoControlCode, + IN LPVOID lpvInBuffer, + IN DWORD cbInBuffer, + OUT LPVOID lpvOutBuffer, + IN DWORD cbOutBuffer, + OUT LPDWORD lpcbBytesReturned, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine), + (s, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, lpOverlapped, lpCompletionRoutine)); +#define WSAIoctl apr_winapi_WSAIoctl + #ifdef SetDllDirectoryW #undef SetDllDirectoryW #endif Index: apr-1.4.8/network_io/unix/sockopt.c =================================================================== --- apr-1.4.8.orig/network_io/unix/sockopt.c +++ apr-1.4.8/network_io/unix/sockopt.c @@ -318,6 +318,30 @@ apr_status_t apr_socket_opt_set(apr_sock return APR_ENOTIMPL; #endif break; + case APR_TCP_KEEPALIVE_IDLE: +#if defined(SO_KEEPALIVE) && defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) + if (on < 0) { + return APR_EINVAL; + } + if ( !!on != apr_is_option_set(sock, APR_SO_KEEPALIVE)) { + rv = apr_socket_opt_set(sock, APR_SO_KEEPALIVE, on); + if (rv != APR_SUCCESS) + return rv; + } + if (on > 0) { + int value = on; + if (setsockopt(sock->socketdes, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&value, sizeof(value)) == -1) { + return errno; + } + value = 1; /* fixed reasonable value */ + if (setsockopt(sock->socketdes, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&value, sizeof(value)) == -1) { + return errno; + } + } +#else + return APR_ENOTIMPL; +#endif + break; default: return APR_EINVAL; } @@ -337,6 +361,8 @@ apr_status_t apr_socket_opt_get(apr_sock apr_int32_t opt, apr_int32_t *on) { switch(opt) { + case APR_TCP_KEEPALIVE_IDLE: + return APR_ENOTIMPL; default: *on = apr_is_option_set(sock, opt); } Index: apr-1.4.8/network_io/win32/sockopt.c =================================================================== --- apr-1.4.8.orig/network_io/win32/sockopt.c +++ apr-1.4.8/network_io/win32/sockopt.c @@ -228,6 +228,30 @@ APR_DECLARE(apr_status_t) apr_socket_opt return APR_ENOTIMPL; #endif break; + case APR_TCP_KEEPALIVE_IDLE: +#ifdef SIO_KEEPALIVE_VALS + if (!APR_HAVE_LATE_DLL_FUNC(WSAIoctl)) { + return APR_ENOTIMPL; + } + if (on < 0) { + return APR_EINVAL; + } + { + struct tcp_keepalive kaparams = {0}; + DWORD outbytes; + kaparams.onoff = !! on; + kaparams.keepalivetime = 1000 * on; + kaparams.keepaliveinterval = 1000; + if (WSAIoctl(sock->socketdes, SIO_KEEPALIVE_VALS, &kaparams, + sizeof(kaparams), NULL, 0, &outbytes, NULL, NULL) != 0) { + return apr_get_netos_error(); + } + apr_set_option(sock, APR_SO_KEEPALIVE, on); + } +#else + return APR_ENOTIMPL; +#endif + break; default: return APR_EINVAL; break; @@ -250,6 +274,8 @@ APR_DECLARE(apr_status_t) apr_socket_opt case APR_SO_DISCONNECTED: *on = sock->disconnected; break; + case APR_TCP_KEEPALIVE_IDLE: + return APR_ENOTIMPL; case APR_SO_KEEPALIVE: case APR_SO_DEBUG: case APR_SO_REUSEADDR: Index: apr-1.4.8/test/testsockopt.c =================================================================== --- apr-1.4.8.orig/test/testsockopt.c +++ apr-1.4.8/test/testsockopt.c @@ -78,6 +78,36 @@ static void remove_keepalive(abts_case * ABTS_INT_EQUAL(tc, 0, ck); } +static void set_keepalive_idle(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_int32_t ck; + + /* NOTE: APR_TCP_KEEPALIVE_IDLE implicitly sets / unsets APR_SO_KEEPALIVE */ + + rv = apr_socket_opt_set(sock, APR_TCP_KEEPALIVE_IDLE, 5); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + rv = apr_socket_opt_get(sock, APR_SO_KEEPALIVE, &ck); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + ABTS_INT_EQUAL(tc, 1, ck); +} + +static void remove_keepalive_idle(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_int32_t ck; + + /* NOTE: APR_TCP_KEEPALIVE_IDLE implicitly sets / unsets APR_SO_KEEPALIVE */ + + rv = apr_socket_opt_set(sock, APR_TCP_KEEPALIVE_IDLE, 0); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + rv = apr_socket_opt_get(sock, APR_SO_KEEPALIVE, &ck); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + ABTS_INT_EQUAL(tc, 0, ck); +} + static void corkable(abts_case *tc, void *data) { #if !APR_HAVE_CORKABLE_TCP @@ -131,6 +161,8 @@ abts_suite *testsockopt(abts_suite *suit abts_run_test(suite, set_keepalive, NULL); abts_run_test(suite, set_debug, NULL); abts_run_test(suite, remove_keepalive, NULL); + abts_run_test(suite, set_keepalive_idle, NULL); + abts_run_test(suite, remove_keepalive_idle, NULL); abts_run_test(suite, corkable, NULL); abts_run_test(suite, close_socket, NULL);