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():
40    os.environ["http_proxy"] = "http://myproxy.example.com:8080"
41    pi = httplib2.proxy_info_from_environment()
42    assert pi.proxy_host == "myproxy.example.com"
43    assert pi.proxy_port == 8080
44
45
46def test_from_env_https():
47    os.environ["http_proxy"] = "http://myproxy.example.com:80"
48    os.environ["https_proxy"] = "http://myproxy.example.com:81"
49    pi = httplib2.proxy_info_from_environment("https")
50    assert pi.proxy_host == "myproxy.example.com"
51    assert pi.proxy_port == 81
52
53
54def test_from_env_none():
55    os.environ.clear()
56    pi = httplib2.proxy_info_from_environment()
57    assert pi is None
58
59
60def test_applies_to():
61    os.environ["http_proxy"] = "http://myproxy.example.com:80"
62    os.environ["https_proxy"] = "http://myproxy.example.com:81"
63    os.environ["no_proxy"] = "localhost,example.com,.wildcard"
64    pi = httplib2.proxy_info_from_environment()
65    assert not pi.applies_to("localhost")
66    assert pi.applies_to("www.google.com")
67    assert pi.applies_to("prefixlocalhost")
68    assert pi.applies_to("www.example.com")
69    assert pi.applies_to("sub.example.com")
70    assert not pi.applies_to("sub.wildcard")
71    assert not pi.applies_to("pub.sub.wildcard")
72
73
74def test_noproxy_trailing_comma():
75    os.environ["http_proxy"] = "http://myproxy.example.com:80"
76    os.environ["no_proxy"] = "localhost,other.host,"
77    pi = httplib2.proxy_info_from_environment()
78    assert not pi.applies_to("localhost")
79    assert not pi.applies_to("other.host")
80    assert pi.applies_to("example.domain")
81
82
83def test_noproxy_star():
84    os.environ["http_proxy"] = "http://myproxy.example.com:80"
85    os.environ["NO_PROXY"] = "*"
86    pi = httplib2.proxy_info_from_environment()
87    for host in ("localhost", "169.254.38.192", "www.google.com"):
88        assert not pi.applies_to(host)
89
90
91def test_headers():
92    headers = {"key0": "val0", "key1": "val1"}
93    pi = httplib2.ProxyInfo(
94        httplib2.socks.PROXY_TYPE_HTTP, "localhost", 1234, proxy_headers=headers
95    )
96    assert pi.proxy_headers == headers
97
98
99@pytest.mark.skipif(
100    os.environ.get("TRAVIS_PYTHON_VERSION") in ("2.7", "pypy"),
101    reason="Fails on Travis py27/pypy, works elsewhere. "
102    "See https://travis-ci.org/httplib2/httplib2/jobs/408769880.",
103)
104@mock.patch("socket.socket.connect", spec=True)
105def test_server_not_found_error_is_raised_for_invalid_hostname(mock_socket_connect):
106    """Invalidates https://github.com/httplib2/httplib2/pull/100."""
107    mock_socket_connect.side_effect = _raise_name_not_known_error
108    http = httplib2.Http(
109        proxy_info=httplib2.ProxyInfo(
110            httplib2.socks.PROXY_TYPE_HTTP, "255.255.255.255", 8001
111        )
112    )
113    with tests.assert_raises(httplib2.ServerNotFoundError):
114        http.request("http://invalid.hostname.foo.bar/", "GET")
115
116
117def test_auth_str_bytes():
118    # https://github.com/httplib2/httplib2/pull/115
119    # Proxy-Authorization b64encode() TypeError: a bytes-like object is required, not 'str'
120    with tests.server_const_http(request_count=2) as uri:
121        uri_parsed = urllib.parse.urlparse(uri)
122        http = httplib2.Http(
123            proxy_info=httplib2.ProxyInfo(
124                httplib2.socks.PROXY_TYPE_HTTP,
125                proxy_host=uri_parsed.hostname,
126                proxy_port=uri_parsed.port,
127                proxy_rdns=True,
128                proxy_user=u"user_str",
129                proxy_pass=u"pass_str",
130            )
131        )
132        response, _ = http.request(uri, "GET")
133        assert response.status == 200
134
135    with tests.server_const_http(request_count=2) as uri:
136        uri_parsed = urllib.parse.urlparse(uri)
137        http = httplib2.Http(
138            proxy_info=httplib2.ProxyInfo(
139                httplib2.socks.PROXY_TYPE_HTTP,
140                proxy_host=uri_parsed.hostname,
141                proxy_port=uri_parsed.port,
142                proxy_rdns=True,
143                proxy_user=b"user_bytes",
144                proxy_pass=b"pass_bytes",
145            )
146        )
147        response, _ = http.request(uri, "GET")
148        assert response.status == 200
149