Adds an `.IOLoop` event handler to accept new connections on ``sock``. When a connection is accepted, ``callback(connection, address)`` will be run (``connection`` is a socket object, and ``address`` is the address of the other end of the connection). Note that this signature is di
(
sock: socket.socket, callback: Callable[[socket.socket, Any], None]
)
| 225 | |
| 226 | |
| 227 | def add_accept_handler( |
| 228 | sock: socket.socket, callback: Callable[[socket.socket, Any], None] |
| 229 | ) -> Callable[[], None]: |
| 230 | """Adds an `.IOLoop` event handler to accept new connections on ``sock``. |
| 231 | |
| 232 | When a connection is accepted, ``callback(connection, address)`` will |
| 233 | be run (``connection`` is a socket object, and ``address`` is the |
| 234 | address of the other end of the connection). Note that this signature |
| 235 | is different from the ``callback(fd, events)`` signature used for |
| 236 | `.IOLoop` handlers. |
| 237 | |
| 238 | A callable is returned which, when called, will remove the `.IOLoop` |
| 239 | event handler and stop processing further incoming connections. |
| 240 | |
| 241 | .. versionchanged:: 5.0 |
| 242 | The ``io_loop`` argument (deprecated since version 4.1) has been removed. |
| 243 | |
| 244 | .. versionchanged:: 5.0 |
| 245 | A callable is returned (``None`` was returned before). |
| 246 | """ |
| 247 | io_loop = IOLoop.current() |
| 248 | removed = [False] |
| 249 | |
| 250 | def accept_handler(fd: socket.socket, events: int) -> None: |
| 251 | # More connections may come in while we're handling callbacks; |
| 252 | # to prevent starvation of other tasks we must limit the number |
| 253 | # of connections we accept at a time. Ideally we would accept |
| 254 | # up to the number of connections that were waiting when we |
| 255 | # entered this method, but this information is not available |
| 256 | # (and rearranging this method to call accept() as many times |
| 257 | # as possible before running any callbacks would have adverse |
| 258 | # effects on load balancing in multiprocess configurations). |
| 259 | # Instead, we use the (default) listen backlog as a rough |
| 260 | # heuristic for the number of connections we can reasonably |
| 261 | # accept at once. |
| 262 | for i in range(_DEFAULT_BACKLOG): |
| 263 | if removed[0]: |
| 264 | # The socket was probably closed |
| 265 | return |
| 266 | try: |
| 267 | connection, address = sock.accept() |
| 268 | except BlockingIOError: |
| 269 | # EWOULDBLOCK indicates we have accepted every |
| 270 | # connection that is available. |
| 271 | return |
| 272 | except ConnectionAbortedError: |
| 273 | # ECONNABORTED indicates that there was a connection |
| 274 | # but it was closed while still in the accept queue. |
| 275 | # (observed on FreeBSD). |
| 276 | continue |
| 277 | callback(connection, address) |
| 278 | |
| 279 | def remove_handler() -> None: |
| 280 | io_loop.remove_handler(sock) |
| 281 | removed[0] = True |
| 282 | |
| 283 | io_loop.add_handler(sock, accept_handler, IOLoop.READ) |
| 284 | return remove_handler |