You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@qpid.apache.org by Alan Conway <ac...@redhat.com> on 2017/02/20 21:23:50 UTC

Picking temporary ports for tests

Our code bases have a motley collection of  attempts to pick safe ports
 for short-lived tests, most of which have race conditions that can
result in sporadic bogus "address in use" test failures.

Here's an approach that seems to work: Bind a socket with bind(0) and
SO_REUSEADDR - guaranteed by the OS to get you a port. Now leave that
socket open while you start your server on that port.  Our servers also
set SO_REUSEADDR for other reasons, this means the server can bind to
the same port even though we have a socket bound already. The test code
only binds, it doesn't listen, so the server is can listen as usual.
Now you can close the test socket and you're off to the races!

Am I only dreaming? Here's the code (proton/proton-c/src/test_tools.h)

/* Create a socket and bind(LOOPBACK:0) to get a free port.
���Use SO_REUSEADDR so other processes can bind and listen on this port.
���Close the returned fd when the other process is listening.
���Fail on error.
*/
static sock_t sock_bind0(void) {
� int sock =��socket(AF_INET, SOCK_STREAM, 0);
� TEST_ASSERT_ERRNO(sock >= 0, errno);
� int on = 1;
� TEST_ASSERT_ERRNO(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == 0, errno);
� struct sockaddr_in addr = {0};
� addr.sin_family = AF_INET;����/* set the type of connection to TCP/IP */
� addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
� addr.sin_port = 0;������������/* bind to port 0 */
� TEST_ASSERT_ERRNO(bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0, errno);
� return sock;
}

/* Get the real port number allocated to a socket created with sock_bind0 */
static int sock_port(sock_t sock) {
� struct sockaddr addr = {0};
� socklen_t len = sizeof(addr);
� TEST_ASSERT_ERRNO(getsockname(sock, &addr, &len) == 0, errno);
� int port = -1;
� switch (addr.sa_family) {
���case AF_INET: port = ((struct sockaddr_in*)&addr)->sin_port; break;
���case AF_INET6: port = ((struct sockaddr_in6*)&addr)->sin6_port; break;
���default: TEST_ASSERTF(false, "unknown protocol type %d\n", addr.sa_family); break;
� }
� return ntohs(port);
}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
For additional commands, e-mail: dev-help@qpid.apache.org


Re: Picking temporary ports for tests

Posted by Alan Conway <ac...@redhat.com>.
On Tue, 2017-02-21 at 09:55 -0500, Chuck Rolke wrote:
> 
> ----- Original Message -----
> > From: "Alan Conway" <ac...@redhat.com>
> > To: "dev" <de...@qpid.apache.org>
> > Sent: Monday, February 20, 2017 4:23:50 PM
> > Subject: Picking temporary ports for tests
> > 
> > Our code bases have a motley collection of��attempts to pick safe
> > ports
> > �for short-lived tests, most of which have race conditions that can
> > result in sporadic bogus "address in use" test failures.
> > 
> > Here's an approach that seems to work: Bind a socket with bind(0)
> > and
> > SO_REUSEADDR - guaranteed by the OS to get you a port. Now leave
> > that
> > socket open while you start your server on that port.��Our servers
> > also
> > set SO_REUSEADDR for other reasons, this means the server can bind
> > to
> > the same port even though we have a socket bound already. The test
> > code
> > only binds, it doesn't listen, so the server is can listen as
> > usual.
> > Now you can close the test socket and you're off to the races!
> > 
> > Am I only dreaming? Here's the code (proton/proton-
> > c/src/test_tools.h)
> > 
> > /* Create a socket and bind(LOOPBACK:0) to get a free port.
> > ���Use SO_REUSEADDR so other processes can bind and listen on this
> > port.
> > ���Close the returned fd when the other process is listening.
> > ���Fail on error.
> > */
> > static sock_t sock_bind0(void) {
> > � int sock =��socket(AF_INET, SOCK_STREAM, 0);
> > � TEST_ASSERT_ERRNO(sock >= 0, errno);
> > � int on = 1;
> > � TEST_ASSERT_ERRNO(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
> > (const
> > � char*)&on, sizeof(on)) == 0, errno);
> > � struct sockaddr_in addr = {0};
> > � addr.sin_family = AF_INET;����/* set the type of connection to
> > TCP/IP */
> > � addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> > � addr.sin_port = 0;������������/* bind to port 0 */
> > � TEST_ASSERT_ERRNO(bind(sock, (struct sockaddr*)&addr,
> > sizeof(addr)) == 0,
> > � errno);
> > � return sock;
> > }
> > 
> > /* Get the real port number allocated to a socket created with
> > sock_bind0 */
> > static int sock_port(sock_t sock) {
> > � struct sockaddr addr = {0};
> > � socklen_t len = sizeof(addr);
> > � TEST_ASSERT_ERRNO(getsockname(sock, &addr, &len) == 0, errno);
> > � int port = -1;
> > � switch (addr.sa_family) {
> > ���case AF_INET: port = ((struct sockaddr_in*)&addr)->sin_port;
> > break;
> > ���case AF_INET6: port = ((struct sockaddr_in6*)&addr)->sin6_port;
> > break;
> > ���default: TEST_ASSERTF(false, "unknown protocol type %d\n",
> > ���addr.sa_family); break;
> > � }
> > � return ntohs(port);
> > }
> > 
> > -----------------------------------------------------------------
> > ----
> > To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
> > For additional commands, e-mail: dev-help@qpid.apache.org
> > 
> > 
> 
> Looks like you already have this code in the (proton) code base.�
> * Does it work?
> * Are there issues with using INADDR_LOOPBACK vs INADDR_ANY?
> * It looks like it opens a port on one sa_family only (IPv4 or IPv6).
> � I remember one of the problems with temp ports as the port being
> � available on IPv4 but not on IPv6. Getting them both open was
> � necessary. This may not be an issue for your use case.

It's working for some new tests I'm writing, it probably needs to be
fleshed out for more general use - your points are valid. I'll add some
IPv6 tests (I should have that anyway) and see how it breaks...

Cheers,
Alan.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
For additional commands, e-mail: dev-help@qpid.apache.org


Re: Picking temporary ports for tests

Posted by Chuck Rolke <cr...@redhat.com>.

----- Original Message -----
> From: "Alan Conway" <ac...@redhat.com>
> To: "dev" <de...@qpid.apache.org>
> Sent: Monday, February 20, 2017 4:23:50 PM
> Subject: Picking temporary ports for tests
> 
> Our code bases have a motley collection of  attempts to pick safe ports
>  for short-lived tests, most of which have race conditions that can
> result in sporadic bogus "address in use" test failures.
> 
> Here's an approach that seems to work: Bind a socket with bind(0) and
> SO_REUSEADDR - guaranteed by the OS to get you a port. Now leave that
> socket open while you start your server on that port.  Our servers also
> set SO_REUSEADDR for other reasons, this means the server can bind to
> the same port even though we have a socket bound already. The test code
> only binds, it doesn't listen, so the server is can listen as usual.
> Now you can close the test socket and you're off to the races!
> 
> Am I only dreaming? Here's the code (proton/proton-c/src/test_tools.h)
> 
> /* Create a socket and bind(LOOPBACK:0) to get a free port.
>    Use SO_REUSEADDR so other processes can bind and listen on this port.
>    Close the returned fd when the other process is listening.
>    Fail on error.
> */
> static sock_t sock_bind0(void) {
>   int sock =  socket(AF_INET, SOCK_STREAM, 0);
>   TEST_ASSERT_ERRNO(sock >= 0, errno);
>   int on = 1;
>   TEST_ASSERT_ERRNO(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const
>   char*)&on, sizeof(on)) == 0, errno);
>   struct sockaddr_in addr = {0};
>   addr.sin_family = AF_INET;    /* set the type of connection to TCP/IP */
>   addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
>   addr.sin_port = 0;            /* bind to port 0 */
>   TEST_ASSERT_ERRNO(bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0,
>   errno);
>   return sock;
> }
> 
> /* Get the real port number allocated to a socket created with sock_bind0 */
> static int sock_port(sock_t sock) {
>   struct sockaddr addr = {0};
>   socklen_t len = sizeof(addr);
>   TEST_ASSERT_ERRNO(getsockname(sock, &addr, &len) == 0, errno);
>   int port = -1;
>   switch (addr.sa_family) {
>    case AF_INET: port = ((struct sockaddr_in*)&addr)->sin_port; break;
>    case AF_INET6: port = ((struct sockaddr_in6*)&addr)->sin6_port; break;
>    default: TEST_ASSERTF(false, "unknown protocol type %d\n",
>    addr.sa_family); break;
>   }
>   return ntohs(port);
> }
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
> For additional commands, e-mail: dev-help@qpid.apache.org
> 
> 

Looks like you already have this code in the (proton) code base. 
* Does it work?
* Are there issues with using INADDR_LOOPBACK vs INADDR_ANY?
* It looks like it opens a port on one sa_family only (IPv4 or IPv6).
  I remember one of the problems with temp ports as the port being
  available on IPv4 but not on IPv6. Getting them both open was
  necessary. This may not be an issue for your use case.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@qpid.apache.org
For additional commands, e-mail: dev-help@qpid.apache.org