summaryrefslogtreecommitdiff
blob: 2af65e84f01628057f3957bb58d6077f50d21745 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
Fixes a hang on test_proxy_rejection.
https://github.com/urllib3/urllib3/commit/087d4de8487379033970898866625c00e0d51c85.patch

From 087d4de8487379033970898866625c00e0d51c85 Mon Sep 17 00:00:00 2001
From: Quentin Pradet <quentin.pradet@gmail.com>
Date: Tue, 3 Nov 2020 17:15:50 +0400
Subject: [PATCH] Fix test_proxy_rejection even with two localhost entries

---
 test/contrib/test_socks.py | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/test/contrib/test_socks.py b/test/contrib/test_socks.py
index 1966513c1..ed716f188 100644
--- a/test/contrib/test_socks.py
+++ b/test/contrib/test_socks.py
@@ -1,8 +1,12 @@
+from __future__ import absolute_import
+
 import socket
 import threading
+from socket import getaddrinfo as real_getaddrinfo
 from test import SHORT_TIMEOUT
 
 import pytest
+import socks as py_socks
 
 from dummyserver.server import DEFAULT_CA, DEFAULT_CERTS
 from dummyserver.testcase import IPV4SocketDummyServerTestCase
@@ -87,6 +91,26 @@ def _address_from_socket(sock):
         raise RuntimeError("Unexpected addr type: %r" % addr_type)
 
 
+def _set_up_fake_getaddrinfo(monkeypatch):
+    # Work around https://github.com/urllib3/urllib3/pull/2034
+    # Nothing prevents localhost to point to two different IPs. For example, in the
+    # Ubuntu set up by GitHub Actions, localhost points both to 127.0.0.1 and ::1.
+    #
+    # In case of failure, PySocks will try the same request on both IPs, but our
+    # handle_socks[45]_negotiation functions don't handle retries, which leads either to
+    # a deadlock or a timeout in case of a failure on the first address.
+    #
+    # However, some tests need to exercise failure. We don't want retries there, but
+    # can't affect PySocks retries via its API. Instead, we monkeypatch PySocks so that
+    # it only sees a single address, which effectively disables retries.
+    def fake_getaddrinfo(addr, port, family, socket_type):
+        gai_list = real_getaddrinfo(addr, port, family, socket_type)
+        gai_list = [gai for gai in gai_list if gai[0] == socket.AF_INET]
+        return gai_list[:1]
+
+    monkeypatch.setattr(py_socks.socket, "getaddrinfo", fake_getaddrinfo)
+
+
 def handle_socks5_negotiation(sock, negotiate, username=None, password=None):
     """
     Handle the SOCKS5 handshake.
@@ -334,7 +358,8 @@ def request_handler(listener):
             with pytest.raises(NewConnectionError):
                 pm.request("GET", "http://example.com", retries=False)
 
-    def test_proxy_rejection(self):
+    def test_proxy_rejection(self, monkeypatch):
+        _set_up_fake_getaddrinfo(monkeypatch)
         evt = threading.Event()
 
         def request_handler(listener):
@@ -429,7 +454,9 @@ def request_handler(listener):
             assert response.data == b""
             assert response.headers["Server"] == "SocksTestServer"
 
-    def test_socks_with_invalid_password(self):
+    def test_socks_with_invalid_password(self, monkeypatch):
+        _set_up_fake_getaddrinfo(monkeypatch)
+
         def request_handler(listener):
             sock = listener.accept()[0]
 
@@ -592,7 +619,8 @@ def request_handler(listener):
             response = pm.request("GET", "http://example.com")
             assert response.status == 200
 
-    def test_proxy_rejection(self):
+    def test_proxy_rejection(self, monkeypatch):
+        _set_up_fake_getaddrinfo(monkeypatch)
         evt = threading.Event()
 
         def request_handler(listener):