• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Proxy tests.
2
3Tests do modify `os.environ` global states. Each test must be run in separate
4process. Must use `pytest --forked` or similar technique.
5"""
6
7from __future__ import absolute_import
8from __future__ import division
9from __future__ import print_function
10
11import httplib2
12import mock
13import os
14import pytest
15import socket
16import tests
17from six.moves import urllib
18
19
20def _raise_name_not_known_error(*args, **kwargs):
21    raise socket.gaierror(socket.EAI_NONAME, "Name or service not known")
22
23
24def test_from_url():
25    pi = httplib2.proxy_info_from_url("http://myproxy.example.com")
26    assert pi.proxy_host == "myproxy.example.com"
27    assert pi.proxy_port == 80
28    assert pi.proxy_user is None
29
30
31def test_from_url_ident():
32    pi = httplib2.proxy_info_from_url("http://zoidberg:fish@someproxy:99")
33    assert pi.proxy_host == "someproxy"
34    assert pi.proxy_port == 99
35    assert pi.proxy_user == "zoidberg"
36    assert pi.proxy_pass == "fish"
37
38
39def test_from_env(monkeypatch):
40    assert os.environ.get("http_proxy") is None
41    monkeypatch.setenv("http_proxy", "http://myproxy.example.com:8080")
42    pi = httplib2.proxy_info_from_environment()
43    assert pi.proxy_host == "myproxy.example.com"
44    assert pi.proxy_port == 8080
45
46
47def test_from_env_https(monkeypatch):
48    assert os.environ.get("http_proxy") is None
49    monkeypatch.setenv("http_proxy", "http://myproxy.example.com:80")
50    monkeypatch.setenv("https_proxy", "http://myproxy.example.com:81")
51    pi = httplib2.proxy_info_from_environment("https")
52    assert pi.proxy_host == "myproxy.example.com"
53    assert pi.proxy_port == 81
54
55
56def test_from_env_none():
57    os.environ.clear()
58    pi = httplib2.proxy_info_from_environment()
59    assert pi is None
60
61
62def test_applies_to(monkeypatch):
63    monkeypatch.setenv("http_proxy", "http://myproxy.example.com:80")
64    monkeypatch.setenv("https_proxy", "http://myproxy.example.com:81")
65    monkeypatch.setenv("no_proxy", "localhost,example.com,.wildcard")
66    pi = httplib2.proxy_info_from_environment()
67    assert not pi.applies_to("localhost")
68    assert pi.applies_to("www.google.com")
69    assert pi.applies_to("prefixlocalhost")
70    assert pi.applies_to("www.example.com")
71    assert pi.applies_to("sub.example.com")
72    assert not pi.applies_to("sub.wildcard")
73    assert not pi.applies_to("pub.sub.wildcard")
74
75
76def test_noproxy_trailing_comma(monkeypatch):
77    monkeypatch.setenv("http_proxy", "http://myproxy.example.com:80")
78    monkeypatch.setenv("no_proxy", "localhost,other.host,")
79    pi = httplib2.proxy_info_from_environment()
80    assert not pi.applies_to("localhost")
81    assert not pi.applies_to("other.host")
82    assert pi.applies_to("example.domain")
83
84
85def test_noproxy_star(monkeypatch):
86    monkeypatch.setenv("http_proxy", "http://myproxy.example.com:80")
87    monkeypatch.setenv("NO_PROXY", "*")
88    pi = httplib2.proxy_info_from_environment()
89    for host in ("localhost", "169.254.38.192", "www.google.com"):
90        assert not pi.applies_to(host)
91
92
93def test_headers():
94    headers = {"key0": "val0", "key1": "val1"}
95    pi = httplib2.ProxyInfo(
96        httplib2.socks.PROXY_TYPE_HTTP, "localhost", 1234, proxy_headers=headers
97    )
98    assert pi.proxy_headers == headers
99
100
101@pytest.mark.skipif(
102    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
103    reason="Fails on Travis py27/pypy, works elsewhere. "
104    "See https://travis-ci.org/httplib2/httplib2/jobs/408769880.",
105)
106@mock.patch("socket.socket.connect", spec=True)
107def test_server_not_found_error_is_raised_for_invalid_hostname(mock_socket_connect):
108    """Invalidates https://github.com/httplib2/httplib2/pull/100."""
109    mock_socket_connect.side_effect = _raise_name_not_known_error
110    http = httplib2.Http(
111        proxy_info=httplib2.ProxyInfo(
112            httplib2.socks.PROXY_TYPE_HTTP, "255.255.255.255", 8001
113        )
114    )
115    with tests.assert_raises(httplib2.ServerNotFoundError):
116        http.request("http://invalid.hostname.foo.bar/", "GET")
117
118
119def test_auth_str_bytes():
120    # https://github.com/httplib2/httplib2/pull/115
121    # Proxy-Authorization b64encode() TypeError: a bytes-like object is required, not 'str'
122    with tests.server_const_http(request_count=2) as uri:
123        uri_parsed = urllib.parse.urlparse(uri)
124        http = httplib2.Http(
125            proxy_info=httplib2.ProxyInfo(
126                httplib2.socks.PROXY_TYPE_HTTP,
127                proxy_host=uri_parsed.hostname,
128                proxy_port=uri_parsed.port,
129                proxy_rdns=True,
130                proxy_user=u"user_str",
131                proxy_pass=u"pass_str",
132            )
133        )
134        response, _ = http.request(uri, "GET")
135        assert response.status == 200
136
137    with tests.server_const_http(request_count=2) as uri:
138        uri_parsed = urllib.parse.urlparse(uri)
139        http = httplib2.Http(
140            proxy_info=httplib2.ProxyInfo(
141                httplib2.socks.PROXY_TYPE_HTTP,
142                proxy_host=uri_parsed.hostname,
143                proxy_port=uri_parsed.port,
144                proxy_rdns=True,
145                proxy_user=b"user_bytes",
146                proxy_pass=b"pass_bytes",
147            )
148        )
149        response, _ = http.request(uri, "GET")
150        assert response.status == 200
151
152
153def test_socks5_auth():
154    def proxy_conn(client, tick):
155        data = client.recv(64)
156        assert data == b"\x05\x02\x00\x02"
157        client.send(b"\x05\x02")  # select username/password auth
158        data = client.recv(64)
159        assert data == b"\x01\x08user_str\x08pass_str"
160        client.send(b"\x01\x01")  # deny
161        tick(None)
162
163    with tests.server_socket(proxy_conn) as uri:
164        uri_parsed = urllib.parse.urlparse(uri)
165        proxy_info = httplib2.ProxyInfo(
166            httplib2.socks.PROXY_TYPE_SOCKS5,
167            proxy_host=uri_parsed.hostname,
168            proxy_port=uri_parsed.port,
169            proxy_rdns=True,
170            proxy_user=u"user_str",
171            proxy_pass=u"pass_str",
172        )
173        http = httplib2.Http(proxy_info=proxy_info)
174        with tests.assert_raises(httplib2.socks.Socks5AuthError):
175            http.request(uri, "GET")
176
177
178def test_functional_noproxy_star_http(monkeypatch):
179    def handler(request):
180        if request.method == "CONNECT":
181            return tests.http_response_bytes(
182                status="400 Expected direct", headers={"connection": "close"},
183            )
184        return tests.http_response_bytes()
185
186    with tests.server_request(handler) as uri:
187        monkeypatch.setenv("http_proxy", uri)
188        monkeypatch.setenv("no_proxy", "*")
189        http = httplib2.Http()
190        response, _ = http.request(uri, "GET")
191        assert response.status == 200
192
193
194def test_functional_noproxy_star_https(monkeypatch):
195    def handler(request):
196        if request.method == "CONNECT":
197            return tests.http_response_bytes(
198                status="400 Expected direct", headers={"connection": "close"},
199            )
200        return tests.http_response_bytes()
201
202    with tests.server_request(handler, tls=True) as uri:
203        monkeypatch.setenv("https_proxy", uri)
204        monkeypatch.setenv("no_proxy", "*")
205        http = httplib2.Http(ca_certs=tests.CA_CERTS)
206        response, _ = http.request(uri, "GET")
207        assert response.status == 200
208