Returns an unused port that should be suitable for binding. This is achieved by creating a temporary socket with the same family and type as the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to the specified host address (defaults to 0.0.0.0) with the port set to 0,
(
family: socket.AddressFamily = socket.AF_INET,
socktype: socket.SocketKind = socket.SOCK_STREAM,
)
| 12 | |
| 13 | |
| 14 | def find_unused_port( |
| 15 | family: socket.AddressFamily = socket.AF_INET, |
| 16 | socktype: socket.SocketKind = socket.SOCK_STREAM, |
| 17 | ) -> int: |
| 18 | """Returns an unused port that should be suitable for binding. This is |
| 19 | achieved by creating a temporary socket with the same family and type as |
| 20 | the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to |
| 21 | the specified host address (defaults to 0.0.0.0) with the port set to 0, |
| 22 | eliciting an unused ephemeral port from the OS. The temporary socket is |
| 23 | then closed and deleted, and the ephemeral port is returned. |
| 24 | |
| 25 | Either this method or bind_port() should be used for any tests where a |
| 26 | server socket needs to be bound to a particular port for the duration of |
| 27 | the test. Which one to use depends on whether the calling code is creating |
| 28 | a python socket, or if an unused port needs to be provided in a constructor |
| 29 | or passed to an external program (i.e. the -accept argument to openssl's |
| 30 | s_server mode). Always prefer bind_port() over find_unused_port() where |
| 31 | possible. Hard coded ports should *NEVER* be used. As soon as a server |
| 32 | socket is bound to a hard coded port, the ability to run multiple instances |
| 33 | of the test simultaneously on the same host is compromised, which makes the |
| 34 | test a ticking time bomb in a buildbot environment. On Unix buildbots, this |
| 35 | may simply manifest as a failed test, which can be recovered from without |
| 36 | intervention in most cases, but on Windows, the entire python process can |
| 37 | completely and utterly wedge, requiring someone to log in to the buildbot |
| 38 | and manually kill the affected process. |
| 39 | |
| 40 | (This is easy to reproduce on Windows, unfortunately, and can be traced to |
| 41 | the SO_REUSEADDR socket option having different semantics on Windows versus |
| 42 | Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind, |
| 43 | listen and then accept connections on identical host/ports. An EADDRINUSE |
| 44 | OSError will be raised at some point (depending on the platform and |
| 45 | the order bind and listen were called on each socket). |
| 46 | |
| 47 | However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE |
| 48 | will ever be raised when attempting to bind two identical host/ports. When |
| 49 | accept() is called on each socket, the second caller's process will steal |
| 50 | the port from the first caller, leaving them both in an awkwardly wedged |
| 51 | state where they'll no longer respond to any signals or graceful kills, and |
| 52 | must be forcibly killed via OpenProcess()/TerminateProcess(). |
| 53 | |
| 54 | The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option |
| 55 | instead of SO_REUSEADDR, which effectively affords the same semantics as |
| 56 | SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open |
| 57 | Source world compared to Windows ones, this is a common mistake. A quick |
| 58 | look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when |
| 59 | openssl.exe is called with the 's_server' option, for example. See |
| 60 | http://bugs.python.org/issue2550 for more info. The following site also |
| 61 | has a very thorough description about the implications of both REUSEADDR |
| 62 | and EXCLUSIVEADDRUSE on Windows: |
| 63 | http://msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx) |
| 64 | |
| 65 | XXX: although this approach is a vast improvement on previous attempts to |
| 66 | elicit unused ports, it rests heavily on the assumption that the ephemeral |
| 67 | port returned to us by the OS won't immediately be dished back out to some |
| 68 | other process when we close and delete our temporary socket but before our |
| 69 | calling code has a chance to bind the returned port. We can deal with this |
| 70 | issue if/when we come across it.""" |
| 71 |