Repository: aio-libs/aiohttp

Issue:
Implement proxy_form_env() function
## Long story short

Custom keyword arguments are not provided to the request class constructor by the _request method on the client session. This prevents certain arguments from being provided (e.g. proxy_from_env) as there's no arguments on the request method for them.

## Expected behaviour

They would be passed.

## Actual behaviour

They aren't.

## Steps to reproduce

```py
async def something():
    async with aiohttp.ClientSession() as sess:
        async with sess.get("https://httpbin.org/ip", proxy_from_env=True) as r:
            return (await r.json())
```

`TypeError: _request() got an unexpected keyword argument **'proxy_from_env'**`

## Your environment

aiohttp 2.2.0 on Python 3.5.3



Discussion:
Please don't do it.
If you need to get proxy settings from environment variables -- use explicit function:
```
def proxy_from_env():
    proxy, proxy_auth = _parse(os.environ)
    return {'proxy': proxy, 'proxy_auth': proxy_auth}

async def something():
    async with aiohttp.ClientSession() as sess:
        async with sess.get("https://httpbin.org/ip", **proxy_from_env()) as r:
            return (await r.json())
```

BTW if you would to make a PR for adding `proxy_from_env()` function to aiohttp -- you are welcome.
we load proxy info from env already. https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client_reqrep.py#L298
Ooops, I've missed this.
So what is the issue?
> we load proxy info from env already.

Only if `proxy_from_env=True` is passed to ClientRequest - which it can't be, because there's no way to pass extra keyword arguments through `_request`.
I see. The feature is not completed.
@fafhrd91 maybe it's time to remove it and replace with `proxy_from_env()` function?
`ClientRequest` is in *gray zone* anyway, we shouldn't support backward compatibility for it.
+1

Resolving patch:
diff --git a/aiohttp/client.py b/aiohttp/client.py
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -8,6 +8,7 @@
 import sys
 import traceback
 import warnings
+from collections.abc import Mapping
 
 from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr
 from yarl import URL
@@ -23,8 +24,8 @@
 from .connector import *  # noqa
 from .connector import TCPConnector
 from .cookiejar import CookieJar
-from .helpers import (PY_35, CeilTimeout, TimeoutHandle, _BaseCoroMixin,
-                      deprecated_noop, sentinel)
+from .helpers import (PY_35, CeilTimeout, ProxyInfo, TimeoutHandle,
+                      _BaseCoroMixin, deprecated_noop, sentinel)
 from .http import WS_KEY, WebSocketReader, WebSocketWriter
 from .streams import FlowControlDataQueue
 
@@ -55,7 +56,7 @@ def __init__(self, *, connector=None, loop=None, cookies=None,
                  version=http.HttpVersion11,
                  cookie_jar=None, connector_owner=True, raise_for_status=False,
                  read_timeout=sentinel, conn_timeout=None,
-                 auto_decompress=True):
+                 auto_decompress=True, proxies=None):
 
         implicit_loop = False
         if loop is None:
@@ -104,6 +105,15 @@ def __init__(self, *, connector=None, loop=None, cookies=None,
         self._conn_timeout = conn_timeout
         self._raise_for_status = raise_for_status
         self._auto_decompress = auto_decompress
+        self._proxies = {}
+        if proxies is not None:
+            for scheme, info in proxies.items():
+                if isinstance(info, ProxyInfo):
+                    pass
+                elif isinstance(info, Mapping):
+                    assert info.keys() == {'proxy', 'proxy_auth'}
+                    info = ProxyInfo(**info)
+                self._proxies[scheme] = info
 
         # Convert to list of tuples
         if headers:
@@ -224,6 +234,7 @@ def _request(self, method, url, *,
             timeout if timeout is not sentinel else self._read_timeout)
         handle = tm.start()
 
+        url = URL(url)
         timer = tm.timer()
         try:
             with timer:
@@ -231,6 +242,13 @@ def _request(self, method, url, *,
                     url = url.with_fragment(None)
                     cookies = self._cookie_jar.filter_cookies(url)
 
+                    if proxy is not None:
+                        proxy = URL(proxy)
+                    elif self._proxies:
+                        proxy_info = self._proxies.get(url.scheme)
+                        if proxy_info is not None:
+                            proxy, proxy_auth = proxy_info
+
                     req = self._request_class(
                         method, url, params=params, headers=headers,
                         skip_auto_headers=skip_headers, data=data,
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -8,7 +8,6 @@
 import warnings
 from hashlib import md5, sha1, sha256
 from http.cookies import CookieError, Morsel
-from urllib.request import getproxies
 
 from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy
 from yarl import URL
@@ -78,7 +77,7 @@ def __init__(self, method, url, *,
                  auth=None, version=http.HttpVersion11, compress=None,
                  chunked=None, expect100=False,
                  loop=None, response_class=None,
-                 proxy=None, proxy_auth=None, proxy_from_env=False,
+                 proxy=None, proxy_auth=None,
                  timer=None, session=None, auto_decompress=True,
                  verify_ssl=None, fingerprint=None, ssl_context=None,
                  proxy_headers=None):
@@ -122,7 +121,7 @@ def __init__(self, method, url, *,
         self.update_cookies(cookies)
         self.update_content_encoding(data)
         self.update_auth(auth)
-        self.update_proxy(proxy, proxy_auth, proxy_from_env, proxy_headers)
+        self.update_proxy(proxy, proxy_auth, proxy_headers)
         self.update_fingerprint(fingerprint)
 
         self.update_body_from_data(data)
@@ -318,10 +317,7 @@ def update_expect_continue(self, expect=False):
         if expect:
             self._continue = helpers.create_future(self.loop)
 
-    def update_proxy(self, proxy, proxy_auth, proxy_from_env, proxy_headers):
-        if proxy_from_env and not proxy:
-            proxy_url = getproxies().get(self.original_url.scheme)
-            proxy = URL(proxy_url) if proxy_url else None
+    def update_proxy(self, proxy, proxy_auth, proxy_headers):
         if proxy and not proxy.scheme == 'http':
             raise ValueError("Only http proxies are supported")
         if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -18,10 +18,13 @@
 from pathlib import Path
 from time import gmtime
 from urllib.parse import quote
+from urllib.request import getproxies
 
 from async_timeout import timeout
+from yarl import URL
 
 from . import hdrs
+from .log import client_logger
 
 
 try:
@@ -40,6 +43,7 @@
 
 
 __all__ = ('BasicAuth', 'create_future', 'parse_mimetype',
+           'proxies_from_env', 'ProxyInfo',
            'Timeout', 'ensure_future', 'noop')
 
 
@@ -169,12 +173,7 @@ def isfuture(fut):
 
 
 class BasicAuth(namedtuple('BasicAuth', ['login', 'password', 'encoding'])):
-    """Http basic authentication helper.
-
-    :param str login: Login
-    :param str password: Password
-    :param str encoding: (optional) encoding ('latin1' by default)
-    """
+    """Http basic authentication helper."""
 
     def __new__(cls, login, password='', encoding='latin1'):
         if login is None:
@@ -191,8 +190,7 @@ def __new__(cls, login, password='', encoding='latin1'):
 
     @classmethod
     def decode(cls, auth_header, encoding='latin1'):
-        """Create a :class:`BasicAuth` object from an ``Authorization`` HTTP
-        header."""
+        """Create a BasicAuth object from an Authorization HTTP header."""
         split = auth_header.strip().split(' ')
         if len(split) == 2:
             if split[0].strip().lower() != 'basic':
@@ -210,12 +208,47 @@ def decode(cls, auth_header, encoding='latin1'):
 
         return cls(username, password, encoding=encoding)
 
+    @classmethod
+    def from_url(cls, url, *, encoding='latin1'):
+        """Create BasicAuth from url."""
+        if not isinstance(url, URL):
+            raise TypeError("url should be yarl.URL instance")
+        if url.user is None:
+            return None
+        return cls(url.user, url.password, encoding=encoding)
+
     def encode(self):
         """Encode credentials."""
         creds = ('%s:%s' % (self.login, self.password)).encode(self.encoding)
         return 'Basic %s' % base64.b64encode(creds).decode(self.encoding)
 
 
+def _strip_auth_from_url(url):
+    auth = BasicAuth.from_url(url)
+    if auth is None:
+        return url, None
+    else:
+        return url.with_user(None), auth
+
+
+ProxyInfo = namedtuple('ProxyInfo', 'proxy proxy_auth')
+
+
+def proxies_from_env():
+    proxy_urls = {k: URL(v) for k, v in getproxies().items()
+                  if k in ('http', 'https')}
+    stripped = {k: _strip_auth_from_url(v) for k, v in proxy_urls.items()}
+    ret = {}
+    for proto, val in stripped.items():
+        proxy, auth = val
+        if proxy.scheme == 'https':
+            client_logger.warning(
+                "HTTPS proxies %s are not supported, ignoring", proxy)
+            continue
+        ret[proto] = ProxyInfo(proxy, auth)
+    return ret
+
+
 if PY_352:
     def create_future(loop):
         return loop.create_future()


Test patch:
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,11 +1,13 @@
 import asyncio
 import datetime
 import gc
+import os
 import sys
 import tempfile
 from unittest import mock
 
 import pytest
+from yarl import URL
 
 from aiohttp import helpers
 
@@ -90,6 +92,8 @@ def test_guess_filename_with_tempfile():
         assert (helpers.guess_filename(fp, 'no-throw') is not None)
 
 
+# ------------------- BasicAuth -----------------------------------
+
 def test_basic_auth1():
     # missing password here
     with pytest.raises(ValueError):
@@ -140,6 +144,18 @@ def test_basic_auth_decode_bad_base64():
         helpers.BasicAuth.decode('Basic bmtpbTpwd2Q')
 
 
+def test_basic_auth_from_url():
+    url = URL('http://user:pass@example.com')
+    auth = helpers.BasicAuth.from_url(url)
+    assert auth.login == 'user'
+    assert auth.password == 'pass'
+
+
+def test_basic_auth_from_not_url():
+    with pytest.raises(TypeError):
+        helpers.BasicAuth.from_url('http://user:pass@example.com')
+
+
 # ------------- access logger -------------------------
 
 
@@ -546,3 +562,44 @@ def test_set_content_disposition_bad_param():
     with pytest.raises(ValueError):
         helpers.content_disposition_header('inline',
                                            **{'foo\x00bar': 'baz'})
+
+
+# --------------------- proxies_from_env ------------------------------
+
+def test_proxies_from_env_http(mocker):
+    url = URL('http://aiohttp.io/path')
+    mocker.patch.dict(os.environ, {'http_proxy': str(url)})
+    ret = helpers.proxies_from_env()
+    assert ret.keys() == {'http'}
+    assert ret['http'].proxy == url
+    assert ret['http'].proxy_auth is None
+
+
+def test_proxies_from_env_http_proxy_for_https_proto(mocker):
+    url = URL('http://aiohttp.io/path')
+    mocker.patch.dict(os.environ, {'https_proxy': str(url)})
+    ret = helpers.proxies_from_env()
+    assert ret.keys() == {'https'}
+    assert ret['https'].proxy == url
+    assert ret['https'].proxy_auth is None
+
+
+def test_proxies_from_env_https_proxy_skipped(mocker):
+    url = URL('https://aiohttp.io/path')
+    mocker.patch.dict(os.environ, {'https_proxy': str(url)})
+    log = mocker.patch('aiohttp.log.client_logger.warning')
+    assert helpers.proxies_from_env() == {}
+    log.assert_called_with('HTTPS proxies %s are not supported, ignoring',
+                           URL('https://aiohttp.io/path'))
+
+
+def test_proxies_from_env_http_with_auth(mocker):
+    url = URL('http://user:pass@aiohttp.io/path')
+    mocker.patch.dict(os.environ, {'http_proxy': str(url)})
+    ret = helpers.proxies_from_env()
+    assert ret.keys() == {'http'}
+    assert ret['http'].proxy == url.with_user(None)
+    proxy_auth = ret['http'].proxy_auth
+    assert proxy_auth.login == 'user'
+    assert proxy_auth.password == 'pass'
+    assert proxy_auth.encoding == 'latin1'
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -1,11 +1,9 @@
 import asyncio
 import gc
 import hashlib
-import os
 import socket
 import unittest
 from unittest import mock
-from urllib.request import getproxies
 
 from yarl import URL
 
@@ -602,162 +600,3 @@ def test_https_auth(self, ClientRequestMock):
         self.loop.run_until_complete(proxy_req.close())
         proxy_resp.close()
         self.loop.run_until_complete(req.close())
-
-    @mock.patch.dict(os.environ, {'http_proxy': 'http://proxy.example.com'})
-    @mock.patch('aiohttp.connector.ClientRequest')
-    def test_connect_env_var_ignored(self, ClientRequestMock):
-        req = ClientRequest(
-            'GET', URL('http://www.python.org'),
-            loop=self.loop)
-        self.assertIsNone(req.proxy)
-
-        # mock all the things!
-        connector = aiohttp.TCPConnector(loop=self.loop)
-        connector._resolve_host = make_mocked_coro([mock.MagicMock()])
-
-        proto = mock.Mock()
-        self.loop.create_connection = make_mocked_coro(
-            (proto.transport, proto))
-        conn = self.loop.run_until_complete(connector.connect(req))
-        self.assertEqual(req.url, URL('http://www.python.org'))
-        self.assertIs(conn._protocol, proto)
-        self.assertIs(conn.transport, proto.transport)
-
-        # we patch only the connector, it should not be called
-        ClientRequestMock.assert_not_called()
-        self.assertIn('http', getproxies())
-
-    @mock.patch.dict(os.environ, {'http_proxy': 'http://proxy.example.com'})
-    @mock.patch('aiohttp.connector.ClientRequest')
-    def test_connect_env_var_used(self, ClientRequestMock):
-        req = ClientRequest(
-            'GET', URL('http://www.python.org'),
-            proxy_from_env=True,
-            loop=self.loop
-        )
-        self.assertEqual(str(req.proxy), 'http://proxy.example.com')
-
-        # mock all the things!
-        connector = aiohttp.TCPConnector(loop=self.loop)
-        connector._resolve_host = make_mocked_coro([mock.MagicMock()])
-
-        proto = mock.Mock(**{
-            'transport.get_extra_info.return_value': False,
-        })
-        self.loop.create_connection = make_mocked_coro(
-            (proto.transport, proto))
-        conn = self.loop.run_until_complete(connector.connect(req))
-        self.assertEqual(req.url, URL('http://www.python.org'))
-        self.assertIs(conn._protocol, proto)
-        self.assertIs(conn.transport, proto.transport)
-
-        ClientRequestMock.assert_called_with(
-            'GET', URL('http://proxy.example.com'),
-            auth=None,
-            fingerprint=None,
-            headers={'Host': 'www.python.org'},
-            ssl_context=None,
-            verify_ssl=None,
-            loop=self.loop)
-        self.assertIn('http', getproxies())
-
-    @mock.patch.dict(os.environ, {'https_proxy': 'http://proxy.example.com'})
-    @mock.patch('aiohttp.connector.ClientRequest')
-    def test_connect_env_var_https_ignored(self, ClientRequestMock):
-        req = ClientRequest(
-            'GET', URL('http://www.python.org'),
-            proxy_from_env=True,
-            loop=self.loop
-        )
-        self.assertIsNone(req.proxy)
-
-        # mock all the things!
-        connector = aiohttp.TCPConnector(loop=self.loop)
-        connector._resolve_host = make_mocked_coro([mock.MagicMock()])
-
-        proto = mock.Mock()
-        self.loop.create_connection = make_mocked_coro(
-            (proto.transport, proto))
-        conn = self.loop.run_until_complete(connector.connect(req))
-        self.assertEqual(req.url, URL('http://www.python.org'))
-        self.assertIs(conn._protocol, proto)
-        self.assertIs(conn.transport, proto.transport)
-
-        # we patch only the connector, it should not be called
-        ClientRequestMock.assert_not_called()
-        self.assertIn('https', getproxies())
-        self.assertNotIn('http', getproxies())
-
-    @mock.patch.dict(os.environ, {'https_proxy': 'http://proxy.example.com'})
-    @mock.patch('aiohttp.connector.ClientRequest')
-    def test_connect_env_var_https_used(self, ClientRequestMock):
-        proxy_req = ClientRequest('GET', URL('http://proxy.example.com'),
-                                  loop=self.loop)
-        ClientRequestMock.return_value = proxy_req
-
-        proxy_resp = ClientResponse('get', URL('http://proxy.example.com'))
-        proxy_resp._loop = self.loop
-        proxy_req.send = send_mock = mock.Mock()
-        send_mock.return_value = proxy_resp
-        proxy_resp.start = make_mocked_coro(mock.Mock(status=200))
-
-        connector = aiohttp.TCPConnector(loop=self.loop)
-        connector._resolve_host = make_mocked_coro(
-            [{'hostname': 'hostname', 'host': '127.0.0.1', 'port': 80,
-              'family': socket.AF_INET, 'proto': 0, 'flags': 0}])
-
-        tr, proto = mock.Mock(), mock.Mock()
-        self.loop.create_connection = make_mocked_coro((tr, proto))
-
-        req = ClientRequest(
-            'GET', URL('https://www.python.org'),
-            proxy_from_env=True,
-            loop=self.loop,
-        )
-        self.loop.run_until_complete(connector._create_connection(req))
-
-        self.assertEqual(req.url.path, '/')
-        self.assertEqual(proxy_req.method, 'CONNECT')
-        self.assertEqual(proxy_req.url, URL('https://www.python.org'))
-        tr.close.assert_called_once_with()
-        tr.get_extra_info.assert_called_with('socket', default=None)
-
-        self.loop.run_until_complete(proxy_req.close())
-        proxy_resp.close()
-        self.loop.run_until_complete(req.close())
-        self.assertIn('https', getproxies())
-
-    @mock.patch.dict(os.environ, {'http_proxy': 'http://proxy23.example.com'})
-    @mock.patch('aiohttp.connector.ClientRequest')
-    def test_connect_env_var_not_overwriting(self, ClientRequestMock):
-        req = ClientRequest(
-            'GET', URL('http://www.python.org'),
-            proxy_from_env=True,
-            proxy=URL('http://proxy.example.com'),
-            loop=self.loop
-        )
-        self.assertEqual(str(req.proxy), 'http://proxy.example.com')
-
-        # mock all the things!
-        connector = aiohttp.TCPConnector(loop=self.loop)
-        connector._resolve_host = make_mocked_coro([mock.MagicMock()])
-
-        proto = mock.Mock(**{
-            'transport.get_extra_info.return_value': False,
-        })
-        self.loop.create_connection = make_mocked_coro(
-            (proto.transport, proto))
-        conn = self.loop.run_until_complete(connector.connect(req))
-        self.assertEqual(req.url, URL('http://www.python.org'))
-        self.assertIs(conn._protocol, proto)
-        self.assertIs(conn.transport, proto.transport)
-
-        ClientRequestMock.assert_called_with(
-            'GET', URL('http://proxy.example.com'),
-            auth=None,
-            fingerprint=None,
-            headers={'Host': 'www.python.org'},
-            loop=self.loop,
-            ssl_context=None,
-            verify_ssl=None)
-        self.assertIn('http', getproxies())
diff --git a/tests/test_proxy_functional.py b/tests/test_proxy_functional.py
--- a/tests/test_proxy_functional.py
+++ b/tests/test_proxy_functional.py
@@ -1,13 +1,12 @@
 import asyncio
-from functools import partial
+import os
 from unittest import mock
 
 import pytest
 from yarl import URL
 
 import aiohttp
-import aiohttp.helpers
-import aiohttp.web
+from aiohttp import web
 
 
 @pytest.fixture
@@ -21,8 +20,10 @@ def proxy_test_server(raw_test_server, loop, monkeypatch):
         headers=None,
         body=None)
 
+    proxy_mock = mock.Mock()
+
     @asyncio.coroutine
-    def proxy_handler(request, proxy_mock):
+    def proxy_handler(request):
         proxy_mock.request = request
         proxy_mock.requests_list.append(request)
 
@@ -39,19 +40,18 @@ def proxy_handler(request, proxy_mock):
 
         response['headers'] = headers
 
-        resp = aiohttp.web.Response(**response)
+        resp = web.Response(**response)
         yield from resp.prepare(request)
-        yield from resp.drain()
+        yield from resp.write_eof()
         return resp
 
     @asyncio.coroutine
     def proxy_server():
-        proxy_mock = mock.Mock()
         proxy_mock.request = None
+        proxy_mock.auth = None
         proxy_mock.requests_list = []
 
-        handler = partial(proxy_handler, proxy_mock=proxy_mock)
-        server = yield from raw_test_server(handler)
+        server = yield from raw_test_server(proxy_handler)
 
         proxy_mock.server = server
         proxy_mock.url = server.make_url('/')
@@ -61,20 +61,20 @@ def proxy_server():
     return proxy_server
 
 
-@asyncio.coroutine
-def _request(method, url, loop=None, **kwargs):
-    client = aiohttp.ClientSession(loop=loop)
-    try:
-        resp = yield from client.request(method, url, **kwargs)
-        yield from resp.release()
-        return resp
-    finally:
-        yield from client.close()
-
-
 @pytest.fixture()
 def get_request(loop):
-    return partial(_request, method='GET', loop=loop)
+    @asyncio.coroutine
+    def _request(method='GET', *, url, proxies=None, **kwargs):
+        connector = aiohttp.TCPConnector(verify_ssl=False, loop=loop)
+        client = aiohttp.ClientSession(connector=connector,
+                                       proxies=proxies)
+        try:
+            resp = yield from client.request(method, url, **kwargs)
+            yield from resp.release()
+            return resp
+        finally:
+            yield from client.close()
+    return _request
 
 
 @asyncio.coroutine
@@ -147,7 +147,7 @@ def test_proxy_http_auth(proxy_test_server, get_request):
     assert 'Authorization' not in proxy.request.headers
     assert 'Proxy-Authorization' not in proxy.request.headers
 
-    auth = aiohttp.helpers.BasicAuth('user', 'pass')
+    auth = aiohttp.BasicAuth('user', 'pass')
     yield from get_request(url=url, auth=auth, proxy=proxy.url)
 
     assert 'Authorization' in proxy.request.headers
@@ -168,7 +168,7 @@ def test_proxy_http_auth(proxy_test_server, get_request):
 @asyncio.coroutine
 def test_proxy_http_auth_utf8(proxy_test_server, get_request):
     url = 'http://aiohttp.io/path'
-    auth = aiohttp.helpers.BasicAuth('юзер', 'пасс', 'utf-8')
+    auth = aiohttp.BasicAuth('юзер', 'пасс', 'utf-8')
     proxy = yield from proxy_test_server()
 
     yield from get_request(url=url, auth=auth, proxy=proxy.url)
@@ -276,7 +276,7 @@ def request(pid):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_connect(proxy_test_server, get_request):
+def xtest_proxy_https_connect(proxy_test_server, get_request):
     proxy = yield from proxy_test_server()
     url = 'https://www.google.com.ua/search?q=aiohttp proxy'
 
@@ -293,7 +293,7 @@ def _test_proxy_https_connect(proxy_test_server, get_request):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_connect_with_port(proxy_test_server, get_request):
+def xtest_proxy_https_connect_with_port(proxy_test_server, get_request):
     proxy = yield from proxy_test_server()
     url = 'https://secure.aiohttp.io:2242/path'
 
@@ -310,7 +310,7 @@ def _test_proxy_https_connect_with_port(proxy_test_server, get_request):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_send_body(proxy_test_server, loop):
+def xtest_proxy_https_send_body(proxy_test_server, loop):
     sess = aiohttp.ClientSession(loop=loop)
     proxy = yield from proxy_test_server()
     proxy.return_value = {'status': 200, 'body': b'1'*(2**20)}
@@ -326,7 +326,7 @@ def _test_proxy_https_send_body(proxy_test_server, loop):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_idna_support(proxy_test_server, get_request):
+def xtest_proxy_https_idna_support(proxy_test_server, get_request):
     url = 'https://éé.com/'
     proxy = yield from proxy_test_server()
 
@@ -365,9 +365,9 @@ def test_proxy_https_bad_response(proxy_test_server, get_request):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_auth(proxy_test_server, get_request):
+def xtest_proxy_https_auth(proxy_test_server, get_request):
     url = 'https://secure.aiohttp.io/path'
-    auth = aiohttp.helpers.BasicAuth('user', 'pass')
+    auth = aiohttp.BasicAuth('user', 'pass')
 
     proxy = yield from proxy_test_server()
     yield from get_request(url=url, proxy=proxy.url)
@@ -409,7 +409,7 @@ def _test_proxy_https_auth(proxy_test_server, get_request):
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_acquired_cleanup(proxy_test_server, loop):
+def xtest_proxy_https_acquired_cleanup(proxy_test_server, loop):
     url = 'https://secure.aiohttp.io/path'
 
     conn = aiohttp.TCPConnector(loop=loop)
@@ -435,7 +435,7 @@ def request():
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_acquired_cleanup_force(proxy_test_server, loop):
+def xtest_proxy_https_acquired_cleanup_force(proxy_test_server, loop):
     url = 'https://secure.aiohttp.io/path'
 
     conn = aiohttp.TCPConnector(force_close=True, loop=loop)
@@ -461,7 +461,7 @@ def request():
 
 # @pytest.mark.xfail
 @asyncio.coroutine
-def _test_proxy_https_multi_conn_limit(proxy_test_server, loop):
+def xtest_proxy_https_multi_conn_limit(proxy_test_server, loop):
     url = 'https://secure.aiohttp.io/path'
     limit, multi_conn_num = 1, 5
 
@@ -505,3 +505,79 @@ def _make_ssl_transport_dummy(self, rawsock, protocol, sslcontext,
     monkeypatch.setattr(
         "asyncio.selector_events.BaseSelectorEventLoop._make_ssl_transport",
         _make_ssl_transport_dummy)
+
+
+@asyncio.coroutine
+def test_proxy_from_env_http(proxy_test_server, get_request, mocker):
+    url = 'http://aiohttp.io/path'
+    proxy = yield from proxy_test_server()
+    mocker.patch.dict(os.environ, {'http_proxy': str(proxy.url)})
+
+    yield from get_request(url=url, proxies=aiohttp.proxies_from_env())
+
+    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'
+    assert 'Proxy-Authorization' not in proxy.request.headers
+
+
+@asyncio.coroutine
+def test_proxy_from_env_http_with_auth(proxy_test_server, get_request, mocker):
+    url = 'http://aiohttp.io/path'
+    proxy = yield from proxy_test_server()
+    auth = aiohttp.BasicAuth('user', 'pass')
+    mocker.patch.dict(os.environ, {'http_proxy':
+                                   str(proxy.url
+                                       .with_user(auth.login)
+                                       .with_password(auth.password))})
+
+    yield from get_request(url=url, proxies=aiohttp.proxies_from_env())
+
+    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'
+    assert proxy.request.headers['Proxy-Authorization'] == auth.encode()
+
+
+@asyncio.coroutine
+def xtest_proxy_from_env_https(proxy_test_server, get_request, mocker):
+    url = 'https://aiohttp.io/path'
+    proxy = yield from proxy_test_server()
+    mocker.patch.dict(os.environ, {'https_proxy': str(proxy.url)})
+
+    yield from get_request(url=url, proxies=aiohttp.proxies_from_env())
+
+    assert len(proxy.requests_list) == 2
+    assert proxy.request.method == 'GET'
+    assert proxy.request.host == 'aiohttp.io'
+    assert proxy.request.path_qs == 'https://aiohttp.io/path'
+    assert 'Proxy-Authorization' not in proxy.request.headers
+
+
+@asyncio.coroutine
+def xtest_proxy_from_env_https_with_auth(proxy_test_server,
+                                         get_request, mocker):
+    url = 'https://aiohttp.io/path'
+    proxy = yield from proxy_test_server()
+    auth = aiohttp.BasicAuth('user', 'pass')
+    mocker.patch.dict(os.environ, {'https_proxy':
+                                   str(proxy.url
+                                       .with_user(auth.login)
+                                       .with_password(auth.password))})
+
+    yield from get_request(url=url, proxies=aiohttp.proxies_from_env())
+
+    assert len(proxy.requests_list) == 2
+
+    assert proxy.request.method == 'GET'
+    assert proxy.request.host == 'aiohttp.io'
+    assert proxy.request.path_qs == '/path'
+    assert 'Proxy-Authorization' not in proxy.request.headers
+
+    r2 = proxy.requests_list[0]
+    assert r2.method == 'CONNECT'
+    assert r2.host == 'aiohttp.io'
+    assert r2.path_qs == '/path'
+    assert r2.headers['Proxy-Authorization'] == auth.encode()