We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
__init__() got an unexpected keyword argument 'assert_hostname'
After upgrading to Version 5.1.1 (recent version 4.2.1) the flask app running on below error:
```shell
Traceback (most recent call last):
  File "\elasticapm\transport\http.py", line 79, in send
    "POST", url, body=data, headers=self._headers, timeout=self._timeout, preload_content=False
  File "\urllib3\poolmanager.py", line 324, in urlopen
    response = conn.urlopen(method, u.request_uri, **kw)
  File "\elasticapm\instrumentation\packages\base.py", line 136, in call_if_sampling
    return wrapped(*args, **kwargs)
  File "\elasticapm\instrumentation\packages\base.py", line 136, in call_if_sampling
    return wrapped(*args, **kwargs)
  File "\urllib3\connectionpool.py", line 588, in urlopen
    conn = self._get_conn(timeout=pool_timeout)
  File "\urllib3\connectionpool.py", line 248, in _get_conn
    return conn or self._new_conn()
  File "\urllib3\connectionpool.py", line 209, in _new_conn
    strict=self.strict, **self.conn_kw)
  File "\urllib3\connection.py", line 114, in __init__
    _HTTPConnection.__init__(self, *args, **kw)
TypeError: __init__() got an unexpected keyword argument 'assert_hostname'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "\elasticapm\transport\base.py", line 208, in _flush
    self.send(data)
  File "\elasticapm\transport\http.py", line 92, in send
    raise TransportException(message, data, print_trace=print_trace)
elasticapm.transport.base.TransportException: Unable to reach APM Server: __init__() got an unexpected keyword argument 'assert_hostname' (url: http://<apm-server url>:8200/intake/v2/events)
```

The NO_PROXY env variable is used.


**To Reproduce**

1. apm.init_app()
2. Running on TransportException

**Expected behavior**: 
Connect to apm-server host and add apm data.

**Environment (please complete the following information)**
- OS: Windows/Linux (Conda environment)
- Python version: 3.6.8
- Framework and version: Flask 1.1.1
- APM Server version: 7.3.0
- Agent version: 5.1.1


**Additional context**

Add any other context about the problem here.

- Agent config options 
    ELASTIC_APM = {
        'SERVICE_NAME': '<name>',
        'SERVER_URL': 'http://%s:8200' % ELASTIC_HOST,
        'SECRET_TOKEN': '',
        'DEBUG': True,
        'ENVIRONMENT': 'development',
        'VERIFY_SERVER_CERT': False,
        'SERVICE_VERSION': app_version
    }
- `requirements.txt`:
aniso8601==6.0.0
apispec==2.0.2
asn1crypto==0.24.0
atomicwrites==1.3.0
attrs==19.1.0
blinker==1.4
blueprint==3.4.2
certifi==2019.3.9
cffi==1.12.2
chardet==3.0.4
Click==7.0
colorama==0.4.1
cryptography==2.6.1
ecdsa==0.13
elastic-apm==4.2.1
elasticsearch==7.0.2
Flask==1.1.1
flask-apispec==0.8.1
Flask-JWT==0.3.2
Flask-PyMongo==2.2.0
flask-restplus==0.12.1
future==0.17.1
hvac==0.7.2
idna==2.8
itsdangerous==1.1.0
Jinja2==2.10.1
jsonschema==3.0.1
MarkupSafe==1.1.1
marshmallow==2.19.2
more-itertools==7.0.0
pluggy==0.9.0
psutil==5.6.1
py==1.8.0
pyasn1==0.4.5
pycparser==2.19
PyJWT==1.4.2
pymongo==3.7.2
pyOpenSSL==19.0.0
pyrsistent==0.14.11
pytest==4.4.0
pytest-flask==0.14.0
pytest-mock==1.10.3
python-dateutil==2.8.0
python3-logstash==0.4.80
pytz==2018.9
PyYAML==5.1.1
requests==2.21.0
rsa==4.0
six==1.12.0
urllib3==1.24.1
webargs==5.2.0
Werkzeug==0.15.1
wincertstore==0.2

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `elasticapm/transport/http.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 #  BSD 3-Clause License
4 #
5 #  Copyright (c) 2019, Elasticsearch BV
6 #  All rights reserved.
7 #
8 #  Redistribution and use in source and binary forms, with or without
9 #  modification, are permitted provided that the following conditions are met:
10 #
11 #  * Redistributions of source code must retain the above copyright notice, this
12 #    list of conditions and the following disclaimer.
13 #
14 #  * Redistributions in binary form must reproduce the above copyright notice,
15 #    this list of conditions and the following disclaimer in the documentation
16 #    and/or other materials provided with the distribution.
17 #
18 #  * Neither the name of the copyright holder nor the names of its
19 #    contributors may be used to endorse or promote products derived from
20 #    this software without specific prior written permission.
21 #
22 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 import hashlib
34 import logging
35 import re
36 import ssl
37 
38 import certifi
39 import urllib3
40 from urllib3.exceptions import MaxRetryError, TimeoutError
41 
42 from elasticapm.transport.base import TransportException
43 from elasticapm.transport.http_base import AsyncHTTPTransportBase, HTTPTransportBase
44 from elasticapm.utils import compat, json_encoder, read_pem_file
45 
46 logger = logging.getLogger("elasticapm.transport.http")
47 
48 
49 class Transport(HTTPTransportBase):
50     def __init__(self, url, **kwargs):
51         super(Transport, self).__init__(url, **kwargs)
52         url_parts = compat.urlparse.urlparse(url)
53         pool_kwargs = {"cert_reqs": "CERT_REQUIRED", "ca_certs": certifi.where(), "block": True}
54         if self._server_cert:
55             pool_kwargs.update(
56                 {"assert_fingerprint": self.cert_fingerprint, "assert_hostname": False, "cert_reqs": ssl.CERT_NONE}
57             )
58             del pool_kwargs["ca_certs"]
59         elif not self._verify_server_cert:
60             pool_kwargs["cert_reqs"] = ssl.CERT_NONE
61             pool_kwargs["assert_hostname"] = False
62         proxies = compat.getproxies_environment()
63         proxy_url = proxies.get("https", proxies.get("http", None))
64         if proxy_url and not compat.proxy_bypass_environment(url_parts.netloc):
65             self.http = urllib3.ProxyManager(proxy_url, **pool_kwargs)
66         else:
67             self.http = urllib3.PoolManager(**pool_kwargs)
68 
69     def send(self, data):
70         response = None
71 
72         if compat.PY2 and isinstance(self._url, compat.text_type):
73             url = self._url.encode("utf-8")
74         else:
75             url = self._url
76         try:
77             try:
78                 response = self.http.urlopen(
79                     "POST", url, body=data, headers=self._headers, timeout=self._timeout, preload_content=False
80                 )
81                 logger.debug("Sent request, url=%s size=%.2fkb status=%s", url, len(data) / 1024.0, response.status)
82             except Exception as e:
83                 print_trace = True
84                 if isinstance(e, MaxRetryError) and isinstance(e.reason, TimeoutError):
85                     message = "Connection to APM Server timed out " "(url: %s, timeout: %s seconds)" % (
86                         self._url,
87                         self._timeout,
88                     )
89                     print_trace = False
90                 else:
91                     message = "Unable to reach APM Server: %s (url: %s)" % (e, self._url)
92                 raise TransportException(message, data, print_trace=print_trace)
93             body = response.read()
94             if response.status >= 400:
95                 if response.status == 429:  # rate-limited
96                     message = "Temporarily rate limited: "
97                     print_trace = False
98                 else:
99                     message = "HTTP %s: " % response.status
100                     print_trace = True
101                 message += body.decode("utf8", errors="replace")
102                 raise TransportException(message, data, print_trace=print_trace)
103             return response.getheader("Location")
104         finally:
105             if response:
106                 response.close()
107 
108     def get_config(self, current_version=None, keys=None):
109         """
110         Gets configuration from a remote APM Server
111 
112         :param current_version: version of the current configuration
113         :param keys: a JSON-serializable dict to identify this instance, e.g.
114                 {
115                     "service": {
116                         "name": "foo",
117                         "environment": "bar"
118                     }
119                 }
120         :return: a three-tuple of new version, config dictionary and validity in seconds.
121                  Any element of the tuple can be None.
122         """
123         url = self._config_url
124         data = json_encoder.dumps(keys).encode("utf-8")
125         headers = self._headers.copy()
126         max_age = 300
127         if current_version:
128             headers["If-None-Match"] = current_version
129         try:
130             response = self.http.urlopen(
131                 "POST", url, body=data, headers=headers, timeout=self._timeout, preload_content=False
132             )
133         except (urllib3.exceptions.RequestError, urllib3.exceptions.HTTPError) as e:
134             logger.debug("HTTP error while fetching remote config: %s", compat.text_type(e))
135             return current_version, None, max_age
136         body = response.read()
137         if "Cache-Control" in response.headers:
138             try:
139                 max_age = int(next(re.finditer(r"max-age=(\d+)", response.headers["Cache-Control"])).groups()[0])
140             except StopIteration:
141                 logger.debug("Could not parse Cache-Control header: %s", response.headers["Cache-Control"])
142         if response.status == 304:
143             # config is unchanged, return
144             logger.debug("Configuration unchanged")
145             return current_version, None, max_age
146         elif response.status >= 400:
147             return None, None, max_age
148 
149         if not body:
150             logger.debug("APM Server answered with empty body and status code %s", response.status)
151             return current_version, None, max_age
152 
153         return response.headers.get("Etag"), json_encoder.loads(body.decode("utf-8")), max_age
154 
155     @property
156     def cert_fingerprint(self):
157         if self._server_cert:
158             with open(self._server_cert, "rb") as f:
159                 cert_data = read_pem_file(f)
160             digest = hashlib.sha256()
161             digest.update(cert_data)
162             return digest.hexdigest()
163         return None
164 
165 
166 class AsyncTransport(AsyncHTTPTransportBase, Transport):
167     async_mode = True
168     sync_transport = Transport
169
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/elasticapm/transport/http.py b/elasticapm/transport/http.py
--- a/elasticapm/transport/http.py
+++ b/elasticapm/transport/http.py
@@ -51,12 +51,12 @@
         super(Transport, self).__init__(url, **kwargs)
         url_parts = compat.urlparse.urlparse(url)
         pool_kwargs = {"cert_reqs": "CERT_REQUIRED", "ca_certs": certifi.where(), "block": True}
-        if self._server_cert:
+        if self._server_cert and url_parts.scheme != "http":
             pool_kwargs.update(
                 {"assert_fingerprint": self.cert_fingerprint, "assert_hostname": False, "cert_reqs": ssl.CERT_NONE}
             )
             del pool_kwargs["ca_certs"]
-        elif not self._verify_server_cert:
+        elif not self._verify_server_cert and url_parts.scheme != "http":
             pool_kwargs["cert_reqs"] = ssl.CERT_NONE
             pool_kwargs["assert_hostname"] = False
         proxies = compat.getproxies_environment()

{"golden_diff": "diff --git a/elasticapm/transport/http.py b/elasticapm/transport/http.py\n--- a/elasticapm/transport/http.py\n+++ b/elasticapm/transport/http.py\n@@ -51,12 +51,12 @@\n         super(Transport, self).__init__(url, **kwargs)\n         url_parts = compat.urlparse.urlparse(url)\n         pool_kwargs = {\"cert_reqs\": \"CERT_REQUIRED\", \"ca_certs\": certifi.where(), \"block\": True}\n-        if self._server_cert:\n+        if self._server_cert and url_parts.scheme != \"http\":\n             pool_kwargs.update(\n                 {\"assert_fingerprint\": self.cert_fingerprint, \"assert_hostname\": False, \"cert_reqs\": ssl.CERT_NONE}\n             )\n             del pool_kwargs[\"ca_certs\"]\n-        elif not self._verify_server_cert:\n+        elif not self._verify_server_cert and url_parts.scheme != \"http\":\n             pool_kwargs[\"cert_reqs\"] = ssl.CERT_NONE\n             pool_kwargs[\"assert_hostname\"] = False\n         proxies = compat.getproxies_environment()\n", "issue": "__init__() got an unexpected keyword argument 'assert_hostname'\nAfter upgrading to Version 5.1.1 (recent version 4.2.1) the flask app running on below error:\r\n```shell\r\nTraceback (most recent call last):\r\n  File \"\\elasticapm\\transport\\http.py\", line 79, in send\r\n    \"POST\", url, body=data, headers=self._headers, timeout=self._timeout, preload_content=False\r\n  File \"\\urllib3\\poolmanager.py\", line 324, in urlopen\r\n    response = conn.urlopen(method, u.request_uri, **kw)\r\n  File \"\\elasticapm\\instrumentation\\packages\\base.py\", line 136, in call_if_sampling\r\n    return wrapped(*args, **kwargs)\r\n  File \"\\elasticapm\\instrumentation\\packages\\base.py\", line 136, in call_if_sampling\r\n    return wrapped(*args, **kwargs)\r\n  File \"\\urllib3\\connectionpool.py\", line 588, in urlopen\r\n    conn = self._get_conn(timeout=pool_timeout)\r\n  File \"\\urllib3\\connectionpool.py\", line 248, in _get_conn\r\n    return conn or self._new_conn()\r\n  File \"\\urllib3\\connectionpool.py\", line 209, in _new_conn\r\n    strict=self.strict, **self.conn_kw)\r\n  File \"\\urllib3\\connection.py\", line 114, in __init__\r\n    _HTTPConnection.__init__(self, *args, **kw)\r\nTypeError: __init__() got an unexpected keyword argument 'assert_hostname'\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"\\elasticapm\\transport\\base.py\", line 208, in _flush\r\n    self.send(data)\r\n  File \"\\elasticapm\\transport\\http.py\", line 92, in send\r\n    raise TransportException(message, data, print_trace=print_trace)\r\nelasticapm.transport.base.TransportException: Unable to reach APM Server: __init__() got an unexpected keyword argument 'assert_hostname' (url: http://<apm-server url>:8200/intake/v2/events)\r\n```\r\n\r\nThe NO_PROXY env variable is used.\r\n\r\n\r\n**To Reproduce**\r\n\r\n1. apm.init_app()\r\n2. Running on TransportException\r\n\r\n**Expected behavior**: \r\nConnect to apm-server host and add apm data.\r\n\r\n**Environment (please complete the following information)**\r\n- OS: Windows/Linux (Conda environment)\r\n- Python version: 3.6.8\r\n- Framework and version: Flask 1.1.1\r\n- APM Server version: 7.3.0\r\n- Agent version: 5.1.1\r\n\r\n\r\n**Additional context**\r\n\r\nAdd any other context about the problem here.\r\n\r\n- Agent config options \r\n    ELASTIC_APM = {\r\n        'SERVICE_NAME': '<name>',\r\n        'SERVER_URL': 'http://%s:8200' % ELASTIC_HOST,\r\n        'SECRET_TOKEN': '',\r\n        'DEBUG': True,\r\n        'ENVIRONMENT': 'development',\r\n        'VERIFY_SERVER_CERT': False,\r\n        'SERVICE_VERSION': app_version\r\n    }\r\n- `requirements.txt`:\r\naniso8601==6.0.0\r\napispec==2.0.2\r\nasn1crypto==0.24.0\r\natomicwrites==1.3.0\r\nattrs==19.1.0\r\nblinker==1.4\r\nblueprint==3.4.2\r\ncertifi==2019.3.9\r\ncffi==1.12.2\r\nchardet==3.0.4\r\nClick==7.0\r\ncolorama==0.4.1\r\ncryptography==2.6.1\r\necdsa==0.13\r\nelastic-apm==4.2.1\r\nelasticsearch==7.0.2\r\nFlask==1.1.1\r\nflask-apispec==0.8.1\r\nFlask-JWT==0.3.2\r\nFlask-PyMongo==2.2.0\r\nflask-restplus==0.12.1\r\nfuture==0.17.1\r\nhvac==0.7.2\r\nidna==2.8\r\nitsdangerous==1.1.0\r\nJinja2==2.10.1\r\njsonschema==3.0.1\r\nMarkupSafe==1.1.1\r\nmarshmallow==2.19.2\r\nmore-itertools==7.0.0\r\npluggy==0.9.0\r\npsutil==5.6.1\r\npy==1.8.0\r\npyasn1==0.4.5\r\npycparser==2.19\r\nPyJWT==1.4.2\r\npymongo==3.7.2\r\npyOpenSSL==19.0.0\r\npyrsistent==0.14.11\r\npytest==4.4.0\r\npytest-flask==0.14.0\r\npytest-mock==1.10.3\r\npython-dateutil==2.8.0\r\npython3-logstash==0.4.80\r\npytz==2018.9\r\nPyYAML==5.1.1\r\nrequests==2.21.0\r\nrsa==4.0\r\nsix==1.12.0\r\nurllib3==1.24.1\r\nwebargs==5.2.0\r\nWerkzeug==0.15.1\r\nwincertstore==0.2\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport hashlib\nimport logging\nimport re\nimport ssl\n\nimport certifi\nimport urllib3\nfrom urllib3.exceptions import MaxRetryError, TimeoutError\n\nfrom elasticapm.transport.base import TransportException\nfrom elasticapm.transport.http_base import AsyncHTTPTransportBase, HTTPTransportBase\nfrom elasticapm.utils import compat, json_encoder, read_pem_file\n\nlogger = logging.getLogger(\"elasticapm.transport.http\")\n\n\nclass Transport(HTTPTransportBase):\n    def __init__(self, url, **kwargs):\n        super(Transport, self).__init__(url, **kwargs)\n        url_parts = compat.urlparse.urlparse(url)\n        pool_kwargs = {\"cert_reqs\": \"CERT_REQUIRED\", \"ca_certs\": certifi.where(), \"block\": True}\n        if self._server_cert:\n            pool_kwargs.update(\n                {\"assert_fingerprint\": self.cert_fingerprint, \"assert_hostname\": False, \"cert_reqs\": ssl.CERT_NONE}\n            )\n            del pool_kwargs[\"ca_certs\"]\n        elif not self._verify_server_cert:\n            pool_kwargs[\"cert_reqs\"] = ssl.CERT_NONE\n            pool_kwargs[\"assert_hostname\"] = False\n        proxies = compat.getproxies_environment()\n        proxy_url = proxies.get(\"https\", proxies.get(\"http\", None))\n        if proxy_url and not compat.proxy_bypass_environment(url_parts.netloc):\n            self.http = urllib3.ProxyManager(proxy_url, **pool_kwargs)\n        else:\n            self.http = urllib3.PoolManager(**pool_kwargs)\n\n    def send(self, data):\n        response = None\n\n        if compat.PY2 and isinstance(self._url, compat.text_type):\n            url = self._url.encode(\"utf-8\")\n        else:\n            url = self._url\n        try:\n            try:\n                response = self.http.urlopen(\n                    \"POST\", url, body=data, headers=self._headers, timeout=self._timeout, preload_content=False\n                )\n                logger.debug(\"Sent request, url=%s size=%.2fkb status=%s\", url, len(data) / 1024.0, response.status)\n            except Exception as e:\n                print_trace = True\n                if isinstance(e, MaxRetryError) and isinstance(e.reason, TimeoutError):\n                    message = \"Connection to APM Server timed out \" \"(url: %s, timeout: %s seconds)\" % (\n                        self._url,\n                        self._timeout,\n                    )\n                    print_trace = False\n                else:\n                    message = \"Unable to reach APM Server: %s (url: %s)\" % (e, self._url)\n                raise TransportException(message, data, print_trace=print_trace)\n            body = response.read()\n            if response.status >= 400:\n                if response.status == 429:  # rate-limited\n                    message = \"Temporarily rate limited: \"\n                    print_trace = False\n                else:\n                    message = \"HTTP %s: \" % response.status\n                    print_trace = True\n                message += body.decode(\"utf8\", errors=\"replace\")\n                raise TransportException(message, data, print_trace=print_trace)\n            return response.getheader(\"Location\")\n        finally:\n            if response:\n                response.close()\n\n    def get_config(self, current_version=None, keys=None):\n        \"\"\"\n        Gets configuration from a remote APM Server\n\n        :param current_version: version of the current configuration\n        :param keys: a JSON-serializable dict to identify this instance, e.g.\n                {\n                    \"service\": {\n                        \"name\": \"foo\",\n                        \"environment\": \"bar\"\n                    }\n                }\n        :return: a three-tuple of new version, config dictionary and validity in seconds.\n                 Any element of the tuple can be None.\n        \"\"\"\n        url = self._config_url\n        data = json_encoder.dumps(keys).encode(\"utf-8\")\n        headers = self._headers.copy()\n        max_age = 300\n        if current_version:\n            headers[\"If-None-Match\"] = current_version\n        try:\n            response = self.http.urlopen(\n                \"POST\", url, body=data, headers=headers, timeout=self._timeout, preload_content=False\n            )\n        except (urllib3.exceptions.RequestError, urllib3.exceptions.HTTPError) as e:\n            logger.debug(\"HTTP error while fetching remote config: %s\", compat.text_type(e))\n            return current_version, None, max_age\n        body = response.read()\n        if \"Cache-Control\" in response.headers:\n            try:\n                max_age = int(next(re.finditer(r\"max-age=(\\d+)\", response.headers[\"Cache-Control\"])).groups()[0])\n            except StopIteration:\n                logger.debug(\"Could not parse Cache-Control header: %s\", response.headers[\"Cache-Control\"])\n        if response.status == 304:\n            # config is unchanged, return\n            logger.debug(\"Configuration unchanged\")\n            return current_version, None, max_age\n        elif response.status >= 400:\n            return None, None, max_age\n\n        if not body:\n            logger.debug(\"APM Server answered with empty body and status code %s\", response.status)\n            return current_version, None, max_age\n\n        return response.headers.get(\"Etag\"), json_encoder.loads(body.decode(\"utf-8\")), max_age\n\n    @property\n    def cert_fingerprint(self):\n        if self._server_cert:\n            with open(self._server_cert, \"rb\") as f:\n                cert_data = read_pem_file(f)\n            digest = hashlib.sha256()\n            digest.update(cert_data)\n            return digest.hexdigest()\n        return None\n\n\nclass AsyncTransport(AsyncHTTPTransportBase, Transport):\n    async_mode = True\n    sync_transport = Transport\n", "path": "elasticapm/transport/http.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport hashlib\nimport logging\nimport re\nimport ssl\n\nimport certifi\nimport urllib3\nfrom urllib3.exceptions import MaxRetryError, TimeoutError\n\nfrom elasticapm.transport.base import TransportException\nfrom elasticapm.transport.http_base import AsyncHTTPTransportBase, HTTPTransportBase\nfrom elasticapm.utils import compat, json_encoder, read_pem_file\n\nlogger = logging.getLogger(\"elasticapm.transport.http\")\n\n\nclass Transport(HTTPTransportBase):\n    def __init__(self, url, **kwargs):\n        super(Transport, self).__init__(url, **kwargs)\n        url_parts = compat.urlparse.urlparse(url)\n        pool_kwargs = {\"cert_reqs\": \"CERT_REQUIRED\", \"ca_certs\": certifi.where(), \"block\": True}\n        if self._server_cert and url_parts.scheme != \"http\":\n            pool_kwargs.update(\n                {\"assert_fingerprint\": self.cert_fingerprint, \"assert_hostname\": False, \"cert_reqs\": ssl.CERT_NONE}\n            )\n            del pool_kwargs[\"ca_certs\"]\n        elif not self._verify_server_cert and url_parts.scheme != \"http\":\n            pool_kwargs[\"cert_reqs\"] = ssl.CERT_NONE\n            pool_kwargs[\"assert_hostname\"] = False\n        proxies = compat.getproxies_environment()\n        proxy_url = proxies.get(\"https\", proxies.get(\"http\", None))\n        if proxy_url and not compat.proxy_bypass_environment(url_parts.netloc):\n            self.http = urllib3.ProxyManager(proxy_url, **pool_kwargs)\n        else:\n            self.http = urllib3.PoolManager(**pool_kwargs)\n\n    def send(self, data):\n        response = None\n\n        if compat.PY2 and isinstance(self._url, compat.text_type):\n            url = self._url.encode(\"utf-8\")\n        else:\n            url = self._url\n        try:\n            try:\n                response = self.http.urlopen(\n                    \"POST\", url, body=data, headers=self._headers, timeout=self._timeout, preload_content=False\n                )\n                logger.debug(\"Sent request, url=%s size=%.2fkb status=%s\", url, len(data) / 1024.0, response.status)\n            except Exception as e:\n                print_trace = True\n                if isinstance(e, MaxRetryError) and isinstance(e.reason, TimeoutError):\n                    message = \"Connection to APM Server timed out \" \"(url: %s, timeout: %s seconds)\" % (\n                        self._url,\n                        self._timeout,\n                    )\n                    print_trace = False\n                else:\n                    message = \"Unable to reach APM Server: %s (url: %s)\" % (e, self._url)\n                raise TransportException(message, data, print_trace=print_trace)\n            body = response.read()\n            if response.status >= 400:\n                if response.status == 429:  # rate-limited\n                    message = \"Temporarily rate limited: \"\n                    print_trace = False\n                else:\n                    message = \"HTTP %s: \" % response.status\n                    print_trace = True\n                message += body.decode(\"utf8\", errors=\"replace\")\n                raise TransportException(message, data, print_trace=print_trace)\n            return response.getheader(\"Location\")\n        finally:\n            if response:\n                response.close()\n\n    def get_config(self, current_version=None, keys=None):\n        \"\"\"\n        Gets configuration from a remote APM Server\n\n        :param current_version: version of the current configuration\n        :param keys: a JSON-serializable dict to identify this instance, e.g.\n                {\n                    \"service\": {\n                        \"name\": \"foo\",\n                        \"environment\": \"bar\"\n                    }\n                }\n        :return: a three-tuple of new version, config dictionary and validity in seconds.\n                 Any element of the tuple can be None.\n        \"\"\"\n        url = self._config_url\n        data = json_encoder.dumps(keys).encode(\"utf-8\")\n        headers = self._headers.copy()\n        max_age = 300\n        if current_version:\n            headers[\"If-None-Match\"] = current_version\n        try:\n            response = self.http.urlopen(\n                \"POST\", url, body=data, headers=headers, timeout=self._timeout, preload_content=False\n            )\n        except (urllib3.exceptions.RequestError, urllib3.exceptions.HTTPError) as e:\n            logger.debug(\"HTTP error while fetching remote config: %s\", compat.text_type(e))\n            return current_version, None, max_age\n        body = response.read()\n        if \"Cache-Control\" in response.headers:\n            try:\n                max_age = int(next(re.finditer(r\"max-age=(\\d+)\", response.headers[\"Cache-Control\"])).groups()[0])\n            except StopIteration:\n                logger.debug(\"Could not parse Cache-Control header: %s\", response.headers[\"Cache-Control\"])\n        if response.status == 304:\n            # config is unchanged, return\n            logger.debug(\"Configuration unchanged\")\n            return current_version, None, max_age\n        elif response.status >= 400:\n            return None, None, max_age\n\n        if not body:\n            logger.debug(\"APM Server answered with empty body and status code %s\", response.status)\n            return current_version, None, max_age\n\n        return response.headers.get(\"Etag\"), json_encoder.loads(body.decode(\"utf-8\")), max_age\n\n    @property\n    def cert_fingerprint(self):\n        if self._server_cert:\n            with open(self._server_cert, \"rb\") as f:\n                cert_data = read_pem_file(f)\n            digest = hashlib.sha256()\n            digest.update(cert_data)\n            return digest.hexdigest()\n        return None\n\n\nclass AsyncTransport(AsyncHTTPTransportBase, Transport):\n    async_mode = True\n    sync_transport = Transport\n", "path": "elasticapm/transport/http.py"}]}