Repository: elastic/apm-agent-python

Issue:
Support for ipv6 address format
**Describe the bug**: 

Exception is thrown when python agent is trying to parse url, that contains ipv6 address.
```
ValueError: too many values to unpack (expected 2)
  File "elasticapm/contrib/django/middleware/__init__.py", line 162, in process_response
    "request",
  File "elasticapm/traces.py", line 333, in set_context
    data = data()
  File "elasticapm/contrib/django/middleware/__init__.py", line 160, in <lambda>
    request, capture_body=self.client.config.capture_body in ("all", "transactions")
  File "elasticapm/contrib/django/client.py", line 137, in get_data_from_request
    result["url"] = get_url_dict(url)
  File "elasticapm/utils/__init__.py", line 84, in get_url_dict
    hostname, port = netloc.split(":")
```

**To Reproduce**

Not sure how can I manually replicate it, but below is the request details that is send to our webapp.

```
curl \
 --compressed \
 -H "Accept: */*" \
 -H "Accept-Encoding: gzip, deflate" \
 -H "Connection: close" \
 -H "Host: [::ffff:a9fe:a9fe]:80" \
 -H "User-Agent: AWS Security Scanner" \
 -H "X-Forwarded-For: 44.224.22.196, 10.255.0.2" \
 -H "X-Forwarded-Port: 443" \
 -H "X-Forwarded-Proto: https" \
 -H "X-Real-Ip: 10.255.0.2" \
 "http://[::ffff:a9fe:a9fe]/latest/dynamic/instance-identity/document"
``` 

**Expected behavior**:

The url containing hostname in ipv6 address, should be parsed correctly and python agent should  successfully process request.

**Environment (please complete the following information)**
- OS: Linux
- Python version: 3.5
- Framework and version [e.g. Django 2.1]: 2.1
- APM Server version: 6.7.1
- Agent version: 5.3.0


Discussion:

Resolving patch:
diff --git a/elasticapm/utils/__init__.py b/elasticapm/utils/__init__.py
--- a/elasticapm/utils/__init__.py
+++ b/elasticapm/utils/__init__.py
@@ -102,21 +102,21 @@ def is_master_process():
 
 
 def get_url_dict(url):
-    scheme, netloc, path, params, query, fragment = compat.urlparse.urlparse(url)
-    if ":" in netloc:
-        hostname, port = netloc.split(":")
-    else:
-        hostname, port = (netloc, None)
+    parse_result = compat.urlparse.urlparse(url)
+
     url_dict = {
         "full": encoding.keyword_field(url),
-        "protocol": scheme + ":",
-        "hostname": encoding.keyword_field(hostname),
-        "pathname": encoding.keyword_field(path),
+        "protocol": parse_result.scheme + ":",
+        "hostname": encoding.keyword_field(parse_result.hostname),
+        "pathname": encoding.keyword_field(parse_result.path),
     }
+
+    port = None if parse_result.port is None else str(parse_result.port)
+
     if port:
         url_dict["port"] = port
-    if query:
-        url_dict["search"] = encoding.keyword_field("?" + query)
+    if parse_result.query:
+        url_dict["search"] = encoding.keyword_field("?" + parse_result.query)
     return url_dict
 
 


Test patch:
diff --git a/tests/utils/tests.py b/tests/utils/tests.py
--- a/tests/utils/tests.py
+++ b/tests/utils/tests.py
@@ -89,6 +89,21 @@ def test_get_url_dict():
             "search": "?de",
             "full": "https://example.com:443/a/b/c?de",
         },
+        "https://[::ffff:a9fe:a9fe]/a/b/c?de": {
+            "protocol": "https:",
+            "hostname": "::ffff:a9fe:a9fe",
+            "pathname": "/a/b/c",
+            "search": "?de",
+            "full": "https://[::ffff:a9fe:a9fe]/a/b/c?de",
+        },
+        "http://[::ffff:a9fe:a9fe]:80/a/b/c?de": {
+            "protocol": "http:",
+            "hostname": "::ffff:a9fe:a9fe",
+            "port": "80",
+            "pathname": "/a/b/c",
+            "search": "?de",
+            "full": "http://[::ffff:a9fe:a9fe]:80/a/b/c?de",
+        },
     }
     for url, expected in data.items():
         assert get_url_dict(url) == expected