Skip to content

Commit

Permalink
Add functional tests for proxy support by client (aio-libs#1218)
Browse files Browse the repository at this point in the history
  • Loading branch information
kserhii committed Nov 22, 2016
1 parent b65ecf4 commit 34e4f4f
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 0 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Sebastian Hüther
SeongSoo Cho
Sergey Ninua
Sergey Skripnick
Serhii Kostel
Simon Kennedy
Sin-Woo Bang
Stanislas Plum
Expand Down
276 changes: 276 additions & 0 deletions tests/test_proxy_functional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
import asyncio
import pytest
from unittest import mock
from functools import partial

from yarl import URL

import aiohttp
import aiohttp.web
import aiohttp.helpers


@asyncio.coroutine
def test_proxy_http_absolute_path(proxy_test_server, get_request):
url = 'http://aiohttp.io/path?query=yes'
proxy = yield from proxy_test_server()

yield from get_request(url=url, proxy=proxy.url)

assert len(proxy.requests_list) == 1
assert proxy.request.method == 'GET'
assert proxy.request.host == 'aiohttp.io'
assert proxy.request.path_qs == 'http://aiohttp.io/path?query=yes'


@asyncio.coroutine
def test_proxy_http_raw_path(proxy_test_server, get_request):
url = 'http://aiohttp.io:2561/space sheep?q=can:fly'
proxy = yield from proxy_test_server()

yield from get_request(url=url, proxy=proxy.url)

assert proxy.request.host == 'aiohttp.io:2561'
assert proxy.request.path_qs == 'http://aiohttp.io:2561/space%20sheep?q=can%3Afly'


@asyncio.coroutine
def test_proxy_http_connection_error(get_request):
url = 'http://aiohttp.io/path'
proxy_url = 'http://localhost:2242/'

with pytest.raises(aiohttp.ProxyConnectionError):
yield from get_request(url=url, proxy=proxy_url)


@asyncio.coroutine
def test_proxy_http_bad_response(proxy_test_server, get_request):
url = 'http://aiohttp.io/path'
proxy = yield from proxy_test_server()
proxy.return_value = dict(
status=502,
headers={'Proxy-Agent': 'TestProxy'})

resp = yield from get_request(url=url, proxy=proxy.url)

assert resp.status == 502
assert resp.headers['Proxy-Agent'] == 'TestProxy'


@asyncio.coroutine
def test_proxy_http_auth(proxy_test_server, get_request):
url = 'http://aiohttp.io/path'
proxy = yield from proxy_test_server()

yield from get_request(url=url, proxy=proxy.url)

assert 'Authorization' not in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

auth = aiohttp.helpers.BasicAuth('user', 'pass')
yield from get_request(url=url, auth=auth, proxy=proxy.url)

assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

yield from get_request(url=url, proxy_auth=auth, proxy=proxy.url)

assert 'Authorization' not in proxy.request.headers
assert 'Proxy-Authorization' in proxy.request.headers

yield from get_request(url=url, auth=auth, proxy_auth=auth, proxy=proxy.url)

assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' in proxy.request.headers


@asyncio.coroutine
def test_proxy_http_auth_utf8(proxy_test_server, get_request):
url = 'http://aiohttp.io/path'
auth = aiohttp.helpers.BasicAuth('юзер', 'пасс', 'utf-8')
proxy = yield from proxy_test_server()

yield from get_request(url=url, auth=auth, proxy=proxy.url)

assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers


@asyncio.coroutine
def test_proxy_http_auth_from_url(proxy_test_server, get_request):
url = 'http://aiohttp.io/path'
proxy = yield from proxy_test_server()

auth_url = URL(url).with_user('user').with_password('pass')
yield from get_request(url=auth_url, proxy=proxy.url)

assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

proxy_url = URL(proxy.url).with_user('user').with_password('pass')
yield from get_request(url=url, proxy=proxy_url)

assert 'Authorization' not in proxy.request.headers
assert 'Proxy-Authorization' in proxy.request.headers


@asyncio.coroutine
def test_proxy_https_connect(proxy_test_server, get_request):
proxy = yield from proxy_test_server()
url = 'https://www.google.com.ua/search?q=aiohttp proxy'

yield from get_request(url=url, proxy=proxy.url)

connect = proxy.requests_list[0]
assert connect.method == 'CONNECT'
assert connect.path == 'www.google.com.ua:443'
assert connect.host == 'www.google.com.ua'

assert proxy.request.host == 'www.google.com.ua'
assert proxy.request.path_qs == '/search?q=aiohttp+proxy'


@asyncio.coroutine
def test_proxy_https_connect_with_port(proxy_test_server, get_request):
proxy = yield from proxy_test_server()
url = 'https://secure.aiohttp.io:2242/path'

yield from get_request(url=url, proxy=proxy.url)

connect = proxy.requests_list[0]
assert connect.method == 'CONNECT'
assert connect.path == 'secure.aiohttp.io:2242'
assert connect.host == 'secure.aiohttp.io:2242'

assert proxy.request.host == 'secure.aiohttp.io:2242'
assert proxy.request.path_qs == '/path'


@asyncio.coroutine
def test_proxy_https_connection_error(get_request):
url = 'https://secure.aiohttp.io/path'
proxy_url = 'http://localhost:2242/'

with pytest.raises(aiohttp.ProxyConnectionError):
yield from get_request(url=url, proxy=proxy_url)


@asyncio.coroutine
def test_proxy_https_bad_response(proxy_test_server, get_request):
url = 'https://secure.aiohttp.io/path'
proxy = yield from proxy_test_server()
proxy.return_value = dict(
status=502,
headers={'Proxy-Agent': 'TestProxy'})

with pytest.raises(aiohttp.HttpProxyError):
yield from get_request(url=url, proxy=proxy.url)

assert len(proxy.requests_list) == 1
assert proxy.request.method == 'CONNECT'
assert proxy.request.path == 'secure.aiohttp.io:443'


@asyncio.coroutine
def test_proxy_https_auth(proxy_test_server, get_request):
url = 'https://secure.aiohttp.io/path'
auth = aiohttp.helpers.BasicAuth('user', 'pass')

proxy = yield from proxy_test_server()
yield from get_request(url=url, proxy=proxy.url)

connect = proxy.requests_list[0]
assert 'Authorization' not in connect.headers
assert 'Proxy-Authorization' not in connect.headers
assert 'Authorization' not in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

proxy = yield from proxy_test_server()
yield from get_request(url=url, auth=auth, proxy=proxy.url)

connect = proxy.requests_list[0]
assert 'Authorization' not in connect.headers
assert 'Proxy-Authorization' not in connect.headers
assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

proxy = yield from proxy_test_server()
yield from get_request(url=url, proxy_auth=auth, proxy=proxy.url)

connect = proxy.requests_list[0]
assert 'Authorization' not in connect.headers
assert 'Proxy-Authorization' in connect.headers
assert 'Authorization' not in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers

proxy = yield from proxy_test_server()
yield from get_request(url=url, auth=auth, proxy_auth=auth, proxy=proxy.url)

connect = proxy.requests_list[0]
assert 'Authorization' not in connect.headers
assert 'Proxy-Authorization' in connect.headers
assert 'Authorization' in proxy.request.headers
assert 'Proxy-Authorization' not in proxy.request.headers


def _patch_ssl_transport(monkeypatch):
"""Make ssl transport substitution to prevent ssl handshake."""
def _make_ssl_transport_dummy(self, rawsock, protocol, sslcontext,
waiter=None, **kwargs):
return self._make_socket_transport(rawsock, protocol, waiter)

monkeypatch.setattr(
"asyncio.selector_events.BaseSelectorEventLoop._make_ssl_transport",
_make_ssl_transport_dummy)


@pytest.fixture
def proxy_test_server(raw_test_server, loop, monkeypatch):
"""Handle all proxy requests and imitate remote server response."""

_patch_ssl_transport(monkeypatch)

default_response = dict(
status=200,
headers=None,
body=None)

@asyncio.coroutine
def proxy_handler(request, proxy_mock):
proxy_mock.request = request
proxy_mock.requests_list.append(request)

response = default_response.copy()
if isinstance(proxy_mock.return_value, dict):
response.update(proxy_mock.return_value)

return aiohttp.web.Response(**response)

@asyncio.coroutine
def proxy_server():
proxy_mock = mock.Mock()
proxy_mock.request = None
proxy_mock.requests_list = []

handler = partial(proxy_handler, proxy_mock=proxy_mock)
server = yield from raw_test_server(handler, loop=loop)

proxy_mock.server = server
proxy_mock.url = server.make_url('/')

return proxy_mock

return proxy_server


@asyncio.coroutine
def _request(method, url, loop=None, **kwargs):
with aiohttp.ClientSession(loop=loop) as client:
resp = yield from client.request(method, url, **kwargs)
yield from resp.release()
return resp


@pytest.fixture()
def get_request(loop):
return partial(_request, method='GET', loop=loop)

0 comments on commit 34e4f4f

Please sign in to comment.