|
30 | 30 | class MimeToolMessage(object):
|
31 | 31 | pass
|
32 | 32 | from threading import Event
|
| 33 | +import select |
33 | 34 | import socket
|
34 | 35 | import ssl
|
35 | 36 |
|
@@ -366,6 +367,72 @@ def socket_handler(listener):
|
366 | 367 | self.assertRaises(ProtocolError, response.read)
|
367 | 368 | self.assertEqual(poolsize, pool.pool.qsize())
|
368 | 369 |
|
| 370 | + def test_connection_closed_on_read_timeout_preload_false(self): |
| 371 | + timed_out = Event() |
| 372 | + |
| 373 | + def socket_handler(listener): |
| 374 | + sock = listener.accept()[0] |
| 375 | + |
| 376 | + # Consume request |
| 377 | + buf = b'' |
| 378 | + while not buf.endswith(b'\r\n\r\n'): |
| 379 | + buf = sock.recv(65535) |
| 380 | + |
| 381 | + # Send partial chunked response and then hang. |
| 382 | + sock.send(( |
| 383 | + 'HTTP/1.1 200 OK\r\n' |
| 384 | + 'Content-Type: text/plain\r\n' |
| 385 | + 'Transfer-Encoding: chunked\r\n' |
| 386 | + '\r\n' |
| 387 | + '8\r\n' |
| 388 | + '12345678\r\n').encode('utf-8') |
| 389 | + ) |
| 390 | + timed_out.wait(5) |
| 391 | + |
| 392 | + # Expect a new request, but keep hold of the old socket to avoid |
| 393 | + # leaking it. Because we don't want to hang this thread, we |
| 394 | + # actually use select.select to confirm that a new request is |
| 395 | + # coming in: this lets us time the thread out. |
| 396 | + rlist, _, _ = select.select([listener], [], [], 1) |
| 397 | + assert rlist |
| 398 | + new_sock = listener.accept()[0] |
| 399 | + |
| 400 | + # Consume request |
| 401 | + buf = b'' |
| 402 | + while not buf.endswith(b'\r\n\r\n'): |
| 403 | + buf = new_sock.recv(65535) |
| 404 | + |
| 405 | + # Send complete chunked response. |
| 406 | + new_sock.send(( |
| 407 | + 'HTTP/1.1 200 OK\r\n' |
| 408 | + 'Content-Type: text/plain\r\n' |
| 409 | + 'Transfer-Encoding: chunked\r\n' |
| 410 | + '\r\n' |
| 411 | + '8\r\n' |
| 412 | + '12345678\r\n' |
| 413 | + '0\r\n\r\n').encode('utf-8') |
| 414 | + ) |
| 415 | + |
| 416 | + new_sock.close() |
| 417 | + sock.close() |
| 418 | + |
| 419 | + self._start_server(socket_handler) |
| 420 | + with HTTPConnectionPool(self.host, self.port) as pool: |
| 421 | + # First request should fail. |
| 422 | + response = pool.urlopen('GET', '/', retries=0, |
| 423 | + preload_content=False, |
| 424 | + timeout=Timeout(connect=1, read=0.001)) |
| 425 | + try: |
| 426 | + self.assertRaises(ReadTimeoutError, response.read) |
| 427 | + finally: |
| 428 | + timed_out.set() |
| 429 | + |
| 430 | + # Second should succeed. |
| 431 | + response = pool.urlopen('GET', '/', retries=0, |
| 432 | + preload_content=False, |
| 433 | + timeout=Timeout(connect=1, read=0.1)) |
| 434 | + self.assertEqual(len(response.read()), 8) |
| 435 | + |
369 | 436 |
|
370 | 437 | class TestProxyManager(SocketDummyServerTestCase):
|
371 | 438 |
|
|
0 commit comments