We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[META 689] Ignore *principal* headers by default
See meta issue for the description and details:
- Meta issue: https://github.com/elastic/apm/issues/689


--- 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/conf/constants.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2019, Elasticsearch BV
4 #  All rights reserved.
5 #
6 #  Redistribution and use in source and binary forms, with or without
7 #  modification, are permitted provided that the following conditions are met:
8 #
9 #  * Redistributions of source code must retain the above copyright notice, this
10 #    list of conditions and the following disclaimer.
11 #
12 #  * Redistributions in binary form must reproduce the above copyright notice,
13 #    this list of conditions and the following disclaimer in the documentation
14 #    and/or other materials provided with the distribution.
15 #
16 #  * Neither the name of the copyright holder nor the names of its
17 #    contributors may be used to endorse or promote products derived from
18 #    this software without specific prior written permission.
19 #
20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 import decimal
32 import re
33 from collections import namedtuple
34 
35 
36 def _starmatch_to_regex(pattern):
37     """
38     This is a duplicate of starmatch_to_regex() in utils/__init__.py
39 
40     Duplication to avoid circular imports
41     """
42     options = re.DOTALL
43     # check if we are case sensitive
44     if pattern.startswith("(?-i)"):
45         pattern = pattern[5:]
46     else:
47         options |= re.IGNORECASE
48     i, n = 0, len(pattern)
49     res = []
50     while i < n:
51         c = pattern[i]
52         i = i + 1
53         if c == "*":
54             res.append(".*")
55         else:
56             res.append(re.escape(c))
57     return re.compile(r"(?:%s)\Z" % "".join(res), options)
58 
59 
60 EVENTS_API_PATH = "intake/v2/events"
61 AGENT_CONFIG_PATH = "config/v1/agents"
62 SERVER_INFO_PATH = ""
63 
64 TRACE_CONTEXT_VERSION = 0
65 TRACEPARENT_HEADER_NAME = "traceparent"
66 TRACEPARENT_LEGACY_HEADER_NAME = "elastic-apm-traceparent"
67 TRACEPARENT_BINARY_HEADER_NAME = "elasticapmtraceparent"
68 TRACESTATE_HEADER_NAME = "tracestate"
69 
70 TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
71 
72 KEYWORD_MAX_LENGTH = 1024
73 LONG_FIELD_MAX_LENGTH = 10000
74 
75 HTTP_WITH_BODY = {"POST", "PUT", "PATCH", "DELETE"}
76 
77 MASK = "[REDACTED]"
78 
79 EXCEPTION_CHAIN_MAX_DEPTH = 50
80 
81 ERROR = "error"
82 TRANSACTION = "transaction"
83 SPAN = "span"
84 METRICSET = "metricset"
85 
86 LABEL_RE = re.compile('[.*"]')
87 
88 HARDCODED_PROCESSORS = ["elasticapm.processors.add_context_lines_to_frames"]
89 
90 BASE_SANITIZE_FIELD_NAMES_UNPROCESSED = [
91     "password",
92     "passwd",
93     "pwd",
94     "secret",
95     "*key",
96     "*token*",
97     "*session*",
98     "*credit*",
99     "*card*",
100     "*auth*",
101     "set-cookie",
102 ]
103 
104 BASE_SANITIZE_FIELD_NAMES = [_starmatch_to_regex(x) for x in BASE_SANITIZE_FIELD_NAMES_UNPROCESSED]
105 
106 OUTCOME = namedtuple("OUTCOME", ["SUCCESS", "FAILURE", "UNKNOWN"])(
107     SUCCESS="success", FAILURE="failure", UNKNOWN="unknown"
108 )
109 
110 try:
111     # Python 2
112     LABEL_TYPES = (bool, int, long, float, decimal.Decimal)
113 except NameError:
114     # Python 3
115     LABEL_TYPES = (bool, int, float, decimal.Decimal)
116 
117 TRACESTATE = namedtuple("TRACESTATE", ["SAMPLE_RATE"])(SAMPLE_RATE="s")
118 TRACE_CONTINUATION_STRATEGY = namedtuple("TRACE_CONTINUATION_STRATEGY", ["CONTINUE", "RESTART", "RESTART_EXTERNAL"])(
119     CONTINUE="continue",
120     RESTART="restart",
121     RESTART_EXTERNAL="restart_external",
122 )
123
```

Path: `elasticapm/utils/__init__.py`
Content:
```
1 #  BSD 3-Clause License
2 #
3 #  Copyright (c) 2012, the Sentry Team, see AUTHORS for more details
4 #  Copyright (c) 2019, Elasticsearch BV
5 #  All rights reserved.
6 #
7 #  Redistribution and use in source and binary forms, with or without
8 #  modification, are permitted provided that the following conditions are met:
9 #
10 #  * Redistributions of source code must retain the above copyright notice, this
11 #    list of conditions and the following disclaimer.
12 #
13 #  * Redistributions in binary form must reproduce the above copyright notice,
14 #    this list of conditions and the following disclaimer in the documentation
15 #    and/or other materials provided with the distribution.
16 #
17 #  * Neither the name of the copyright holder nor the names of its
18 #    contributors may be used to endorse or promote products derived from
19 #    this software without specific prior written permission.
20 #
21 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 
31 import base64
32 import os
33 import re
34 import urllib.parse
35 from functools import partial
36 from types import FunctionType
37 from typing import Pattern
38 
39 from elasticapm.conf import constants
40 from elasticapm.utils import encoding
41 
42 try:
43     from functools import partialmethod
44 
45     partial_types = (partial, partialmethod)
46 except ImportError:
47     # Python 2
48     partial_types = (partial,)
49 
50 
51 default_ports = {"https": 443, "http": 80, "postgresql": 5432, "mysql": 3306, "mssql": 1433}
52 
53 
54 def varmap(func, var, context=None, name=None, **kwargs):
55     """
56     Executes ``func(key_name, value)`` on all values,
57     recursively discovering dict and list scoped
58     values.
59     """
60     if context is None:
61         context = set()
62     objid = id(var)
63     if objid in context:
64         return func(name, "<...>", **kwargs)
65     context.add(objid)
66 
67     # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values
68     ret = func(name, var, **kwargs)
69     if isinstance(ret, dict):
70         # iterate over a copy of the dictionary to avoid "dictionary changed size during iteration" issues
71         ret = dict((k, varmap(func, v, context, k, **kwargs)) for k, v in ret.copy().items())
72     elif isinstance(ret, (list, tuple)):
73         # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values
74         ret = [varmap(func, f, context, name, **kwargs) for f in ret]
75     context.remove(objid)
76     return ret
77 
78 
79 def get_name_from_func(func: FunctionType) -> str:
80     # partials don't have `__module__` or `__name__`, so we use the values from the "inner" function
81     if isinstance(func, partial_types):
82         return "partial({})".format(get_name_from_func(func.func))
83     elif hasattr(func, "_partialmethod") and hasattr(func._partialmethod, "func"):
84         return "partial({})".format(get_name_from_func(func._partialmethod.func))
85 
86     module = func.__module__
87 
88     if hasattr(func, "view_class"):
89         view_name = func.view_class.__name__
90     elif hasattr(func, "__name__"):
91         view_name = func.__name__
92     else:  # Fall back if there's no __name__
93         view_name = func.__class__.__name__
94 
95     return "{0}.{1}".format(module, view_name)
96 
97 
98 def build_name_with_http_method_prefix(name, request):
99     return " ".join((request.method, name)) if name else name
100 
101 
102 def is_master_process() -> bool:
103     # currently only recognizes uwsgi master process
104     try:
105         import uwsgi
106 
107         return os.getpid() == uwsgi.masterpid()
108     except ImportError:
109         return False
110 
111 
112 def get_url_dict(url: str) -> dict:
113     parse_result = urllib.parse.urlparse(url)
114 
115     url_dict = {
116         "full": encoding.keyword_field(url),
117         "protocol": parse_result.scheme + ":",
118         "hostname": encoding.keyword_field(parse_result.hostname),
119         "pathname": encoding.keyword_field(parse_result.path),
120     }
121 
122     port = None if parse_result.port is None else str(parse_result.port)
123 
124     if port:
125         url_dict["port"] = port
126     if parse_result.query:
127         url_dict["search"] = encoding.keyword_field("?" + parse_result.query)
128     return url_dict
129 
130 
131 def sanitize_url(url: str) -> str:
132     if "@" not in url:
133         return url
134     parts = urllib.parse.urlparse(url)
135     return url.replace("%s:%s" % (parts.username, parts.password), "%s:%s" % (parts.username, constants.MASK))
136 
137 
138 def get_host_from_url(url: str) -> str:
139     parsed_url = urllib.parse.urlparse(url)
140     host = parsed_url.hostname or " "
141 
142     if parsed_url.port and default_ports.get(parsed_url.scheme) != parsed_url.port:
143         host += ":" + str(parsed_url.port)
144 
145     return host
146 
147 
148 def url_to_destination_resource(url: str) -> str:
149     parts = urllib.parse.urlsplit(url)
150     hostname = parts.hostname if parts.hostname else ""
151     # preserve brackets for IPv6 URLs
152     if "://[" in url:
153         hostname = "[%s]" % hostname
154     try:
155         port = parts.port
156     except ValueError:
157         # Malformed port, just use None rather than raising an exception
158         port = None
159     default_port = default_ports.get(parts.scheme, None)
160     name = "%s://%s" % (parts.scheme, hostname)
161     resource = hostname
162     if not port and parts.scheme in default_ports:
163         port = default_ports[parts.scheme]
164     if port:
165         if port != default_port:
166             name += ":%d" % port
167         resource += ":%d" % port
168     return resource
169 
170 
171 def read_pem_file(file_obj) -> bytes:
172     cert = b""
173     for line in file_obj:
174         if line.startswith(b"-----BEGIN CERTIFICATE-----"):
175             break
176     # scan until we find the first END CERTIFICATE marker
177     for line in file_obj:
178         if line.startswith(b"-----END CERTIFICATE-----"):
179             break
180         cert += line.strip()
181     return base64.b64decode(cert)
182 
183 
184 def starmatch_to_regex(pattern: str) -> Pattern:
185     options = re.DOTALL
186     # check if we are case sensitive
187     if pattern.startswith("(?-i)"):
188         pattern = pattern[5:]
189     else:
190         options |= re.IGNORECASE
191     i, n = 0, len(pattern)
192     res = []
193     while i < n:
194         c = pattern[i]
195         i = i + 1
196         if c == "*":
197             res.append(".*")
198         else:
199             res.append(re.escape(c))
200     return re.compile(r"(?:%s)\Z" % "".join(res), options)
201 
202 
203 def nested_key(d: dict, *args):
204     """
205     Traverses a dictionary for nested keys. Returns `None` if the at any point
206     in the traversal a key cannot be found.
207 
208     Example:
209 
210         >>> from elasticapm.utils import nested_key
211         >>> d = {"a": {"b": {"c": 0}}}
212         >>> nested_key(d, "a", "b", "c")
213         0
214         >>> nested_key(d, "a", "b", "d")
215         None
216     """
217     for arg in args:
218         try:
219             d = d[arg]
220         except (TypeError, KeyError):
221             d = None
222             break
223     return d
224
```
--- 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/conf/constants.py b/elasticapm/conf/constants.py
--- a/elasticapm/conf/constants.py
+++ b/elasticapm/conf/constants.py
@@ -31,16 +31,17 @@
 import decimal
 import re
 from collections import namedtuple
+from typing import Pattern
 
 
-def _starmatch_to_regex(pattern):
+def _starmatch_to_regex(pattern: str) -> Pattern:
     """
     This is a duplicate of starmatch_to_regex() in utils/__init__.py
 
     Duplication to avoid circular imports
     """
     options = re.DOTALL
-    # check if we are case sensitive
+    # check if we are case-sensitive
     if pattern.startswith("(?-i)"):
         pattern = pattern[5:]
     else:
@@ -98,6 +99,7 @@
     "*credit*",
     "*card*",
     "*auth*",
+    "*principal*",
     "set-cookie",
 ]
 
@@ -107,12 +109,7 @@
     SUCCESS="success", FAILURE="failure", UNKNOWN="unknown"
 )
 
-try:
-    # Python 2
-    LABEL_TYPES = (bool, int, long, float, decimal.Decimal)
-except NameError:
-    # Python 3
-    LABEL_TYPES = (bool, int, float, decimal.Decimal)
+LABEL_TYPES = (bool, int, float, decimal.Decimal)
 
 TRACESTATE = namedtuple("TRACESTATE", ["SAMPLE_RATE"])(SAMPLE_RATE="s")
 TRACE_CONTINUATION_STRATEGY = namedtuple("TRACE_CONTINUATION_STRATEGY", ["CONTINUE", "RESTART", "RESTART_EXTERNAL"])(
diff --git a/elasticapm/utils/__init__.py b/elasticapm/utils/__init__.py
--- a/elasticapm/utils/__init__.py
+++ b/elasticapm/utils/__init__.py
@@ -183,7 +183,7 @@
 
 def starmatch_to_regex(pattern: str) -> Pattern:
     options = re.DOTALL
-    # check if we are case sensitive
+    # check if we are case-sensitive
     if pattern.startswith("(?-i)"):
         pattern = pattern[5:]
     else:

{"golden_diff": "diff --git a/elasticapm/conf/constants.py b/elasticapm/conf/constants.py\n--- a/elasticapm/conf/constants.py\n+++ b/elasticapm/conf/constants.py\n@@ -31,16 +31,17 @@\n import decimal\n import re\n from collections import namedtuple\n+from typing import Pattern\n \n \n-def _starmatch_to_regex(pattern):\n+def _starmatch_to_regex(pattern: str) -> Pattern:\n     \"\"\"\n     This is a duplicate of starmatch_to_regex() in utils/__init__.py\n \n     Duplication to avoid circular imports\n     \"\"\"\n     options = re.DOTALL\n-    # check if we are case sensitive\n+    # check if we are case-sensitive\n     if pattern.startswith(\"(?-i)\"):\n         pattern = pattern[5:]\n     else:\n@@ -98,6 +99,7 @@\n     \"*credit*\",\n     \"*card*\",\n     \"*auth*\",\n+    \"*principal*\",\n     \"set-cookie\",\n ]\n \n@@ -107,12 +109,7 @@\n     SUCCESS=\"success\", FAILURE=\"failure\", UNKNOWN=\"unknown\"\n )\n \n-try:\n-    # Python 2\n-    LABEL_TYPES = (bool, int, long, float, decimal.Decimal)\n-except NameError:\n-    # Python 3\n-    LABEL_TYPES = (bool, int, float, decimal.Decimal)\n+LABEL_TYPES = (bool, int, float, decimal.Decimal)\n \n TRACESTATE = namedtuple(\"TRACESTATE\", [\"SAMPLE_RATE\"])(SAMPLE_RATE=\"s\")\n TRACE_CONTINUATION_STRATEGY = namedtuple(\"TRACE_CONTINUATION_STRATEGY\", [\"CONTINUE\", \"RESTART\", \"RESTART_EXTERNAL\"])(\ndiff --git a/elasticapm/utils/__init__.py b/elasticapm/utils/__init__.py\n--- a/elasticapm/utils/__init__.py\n+++ b/elasticapm/utils/__init__.py\n@@ -183,7 +183,7 @@\n \n def starmatch_to_regex(pattern: str) -> Pattern:\n     options = re.DOTALL\n-    # check if we are case sensitive\n+    # check if we are case-sensitive\n     if pattern.startswith(\"(?-i)\"):\n         pattern = pattern[5:]\n     else:\n", "issue": "[META 689] Ignore *principal* headers by default\nSee meta issue for the description and details:\r\n- Meta issue: https://github.com/elastic/apm/issues/689\r\n\n", "before_files": [{"content": "#  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 decimal\nimport re\nfrom collections import namedtuple\n\n\ndef _starmatch_to_regex(pattern):\n    \"\"\"\n    This is a duplicate of starmatch_to_regex() in utils/__init__.py\n\n    Duplication to avoid circular imports\n    \"\"\"\n    options = re.DOTALL\n    # check if we are case sensitive\n    if pattern.startswith(\"(?-i)\"):\n        pattern = pattern[5:]\n    else:\n        options |= re.IGNORECASE\n    i, n = 0, len(pattern)\n    res = []\n    while i < n:\n        c = pattern[i]\n        i = i + 1\n        if c == \"*\":\n            res.append(\".*\")\n        else:\n            res.append(re.escape(c))\n    return re.compile(r\"(?:%s)\\Z\" % \"\".join(res), options)\n\n\nEVENTS_API_PATH = \"intake/v2/events\"\nAGENT_CONFIG_PATH = \"config/v1/agents\"\nSERVER_INFO_PATH = \"\"\n\nTRACE_CONTEXT_VERSION = 0\nTRACEPARENT_HEADER_NAME = \"traceparent\"\nTRACEPARENT_LEGACY_HEADER_NAME = \"elastic-apm-traceparent\"\nTRACEPARENT_BINARY_HEADER_NAME = \"elasticapmtraceparent\"\nTRACESTATE_HEADER_NAME = \"tracestate\"\n\nTIMESTAMP_FORMAT = \"%Y-%m-%dT%H:%M:%S.%fZ\"\n\nKEYWORD_MAX_LENGTH = 1024\nLONG_FIELD_MAX_LENGTH = 10000\n\nHTTP_WITH_BODY = {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n\nMASK = \"[REDACTED]\"\n\nEXCEPTION_CHAIN_MAX_DEPTH = 50\n\nERROR = \"error\"\nTRANSACTION = \"transaction\"\nSPAN = \"span\"\nMETRICSET = \"metricset\"\n\nLABEL_RE = re.compile('[.*\"]')\n\nHARDCODED_PROCESSORS = [\"elasticapm.processors.add_context_lines_to_frames\"]\n\nBASE_SANITIZE_FIELD_NAMES_UNPROCESSED = [\n    \"password\",\n    \"passwd\",\n    \"pwd\",\n    \"secret\",\n    \"*key\",\n    \"*token*\",\n    \"*session*\",\n    \"*credit*\",\n    \"*card*\",\n    \"*auth*\",\n    \"set-cookie\",\n]\n\nBASE_SANITIZE_FIELD_NAMES = [_starmatch_to_regex(x) for x in BASE_SANITIZE_FIELD_NAMES_UNPROCESSED]\n\nOUTCOME = namedtuple(\"OUTCOME\", [\"SUCCESS\", \"FAILURE\", \"UNKNOWN\"])(\n    SUCCESS=\"success\", FAILURE=\"failure\", UNKNOWN=\"unknown\"\n)\n\ntry:\n    # Python 2\n    LABEL_TYPES = (bool, int, long, float, decimal.Decimal)\nexcept NameError:\n    # Python 3\n    LABEL_TYPES = (bool, int, float, decimal.Decimal)\n\nTRACESTATE = namedtuple(\"TRACESTATE\", [\"SAMPLE_RATE\"])(SAMPLE_RATE=\"s\")\nTRACE_CONTINUATION_STRATEGY = namedtuple(\"TRACE_CONTINUATION_STRATEGY\", [\"CONTINUE\", \"RESTART\", \"RESTART_EXTERNAL\"])(\n    CONTINUE=\"continue\",\n    RESTART=\"restart\",\n    RESTART_EXTERNAL=\"restart_external\",\n)\n", "path": "elasticapm/conf/constants.py"}, {"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2012, the Sentry Team, see AUTHORS for more details\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\nimport base64\nimport os\nimport re\nimport urllib.parse\nfrom functools import partial\nfrom types import FunctionType\nfrom typing import Pattern\n\nfrom elasticapm.conf import constants\nfrom elasticapm.utils import encoding\n\ntry:\n    from functools import partialmethod\n\n    partial_types = (partial, partialmethod)\nexcept ImportError:\n    # Python 2\n    partial_types = (partial,)\n\n\ndefault_ports = {\"https\": 443, \"http\": 80, \"postgresql\": 5432, \"mysql\": 3306, \"mssql\": 1433}\n\n\ndef varmap(func, var, context=None, name=None, **kwargs):\n    \"\"\"\n    Executes ``func(key_name, value)`` on all values,\n    recursively discovering dict and list scoped\n    values.\n    \"\"\"\n    if context is None:\n        context = set()\n    objid = id(var)\n    if objid in context:\n        return func(name, \"<...>\", **kwargs)\n    context.add(objid)\n\n    # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values\n    ret = func(name, var, **kwargs)\n    if isinstance(ret, dict):\n        # iterate over a copy of the dictionary to avoid \"dictionary changed size during iteration\" issues\n        ret = dict((k, varmap(func, v, context, k, **kwargs)) for k, v in ret.copy().items())\n    elif isinstance(ret, (list, tuple)):\n        # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values\n        ret = [varmap(func, f, context, name, **kwargs) for f in ret]\n    context.remove(objid)\n    return ret\n\n\ndef get_name_from_func(func: FunctionType) -> str:\n    # partials don't have `__module__` or `__name__`, so we use the values from the \"inner\" function\n    if isinstance(func, partial_types):\n        return \"partial({})\".format(get_name_from_func(func.func))\n    elif hasattr(func, \"_partialmethod\") and hasattr(func._partialmethod, \"func\"):\n        return \"partial({})\".format(get_name_from_func(func._partialmethod.func))\n\n    module = func.__module__\n\n    if hasattr(func, \"view_class\"):\n        view_name = func.view_class.__name__\n    elif hasattr(func, \"__name__\"):\n        view_name = func.__name__\n    else:  # Fall back if there's no __name__\n        view_name = func.__class__.__name__\n\n    return \"{0}.{1}\".format(module, view_name)\n\n\ndef build_name_with_http_method_prefix(name, request):\n    return \" \".join((request.method, name)) if name else name\n\n\ndef is_master_process() -> bool:\n    # currently only recognizes uwsgi master process\n    try:\n        import uwsgi\n\n        return os.getpid() == uwsgi.masterpid()\n    except ImportError:\n        return False\n\n\ndef get_url_dict(url: str) -> dict:\n    parse_result = urllib.parse.urlparse(url)\n\n    url_dict = {\n        \"full\": encoding.keyword_field(url),\n        \"protocol\": parse_result.scheme + \":\",\n        \"hostname\": encoding.keyword_field(parse_result.hostname),\n        \"pathname\": encoding.keyword_field(parse_result.path),\n    }\n\n    port = None if parse_result.port is None else str(parse_result.port)\n\n    if port:\n        url_dict[\"port\"] = port\n    if parse_result.query:\n        url_dict[\"search\"] = encoding.keyword_field(\"?\" + parse_result.query)\n    return url_dict\n\n\ndef sanitize_url(url: str) -> str:\n    if \"@\" not in url:\n        return url\n    parts = urllib.parse.urlparse(url)\n    return url.replace(\"%s:%s\" % (parts.username, parts.password), \"%s:%s\" % (parts.username, constants.MASK))\n\n\ndef get_host_from_url(url: str) -> str:\n    parsed_url = urllib.parse.urlparse(url)\n    host = parsed_url.hostname or \" \"\n\n    if parsed_url.port and default_ports.get(parsed_url.scheme) != parsed_url.port:\n        host += \":\" + str(parsed_url.port)\n\n    return host\n\n\ndef url_to_destination_resource(url: str) -> str:\n    parts = urllib.parse.urlsplit(url)\n    hostname = parts.hostname if parts.hostname else \"\"\n    # preserve brackets for IPv6 URLs\n    if \"://[\" in url:\n        hostname = \"[%s]\" % hostname\n    try:\n        port = parts.port\n    except ValueError:\n        # Malformed port, just use None rather than raising an exception\n        port = None\n    default_port = default_ports.get(parts.scheme, None)\n    name = \"%s://%s\" % (parts.scheme, hostname)\n    resource = hostname\n    if not port and parts.scheme in default_ports:\n        port = default_ports[parts.scheme]\n    if port:\n        if port != default_port:\n            name += \":%d\" % port\n        resource += \":%d\" % port\n    return resource\n\n\ndef read_pem_file(file_obj) -> bytes:\n    cert = b\"\"\n    for line in file_obj:\n        if line.startswith(b\"-----BEGIN CERTIFICATE-----\"):\n            break\n    # scan until we find the first END CERTIFICATE marker\n    for line in file_obj:\n        if line.startswith(b\"-----END CERTIFICATE-----\"):\n            break\n        cert += line.strip()\n    return base64.b64decode(cert)\n\n\ndef starmatch_to_regex(pattern: str) -> Pattern:\n    options = re.DOTALL\n    # check if we are case sensitive\n    if pattern.startswith(\"(?-i)\"):\n        pattern = pattern[5:]\n    else:\n        options |= re.IGNORECASE\n    i, n = 0, len(pattern)\n    res = []\n    while i < n:\n        c = pattern[i]\n        i = i + 1\n        if c == \"*\":\n            res.append(\".*\")\n        else:\n            res.append(re.escape(c))\n    return re.compile(r\"(?:%s)\\Z\" % \"\".join(res), options)\n\n\ndef nested_key(d: dict, *args):\n    \"\"\"\n    Traverses a dictionary for nested keys. Returns `None` if the at any point\n    in the traversal a key cannot be found.\n\n    Example:\n\n        >>> from elasticapm.utils import nested_key\n        >>> d = {\"a\": {\"b\": {\"c\": 0}}}\n        >>> nested_key(d, \"a\", \"b\", \"c\")\n        0\n        >>> nested_key(d, \"a\", \"b\", \"d\")\n        None\n    \"\"\"\n    for arg in args:\n        try:\n            d = d[arg]\n        except (TypeError, KeyError):\n            d = None\n            break\n    return d\n", "path": "elasticapm/utils/__init__.py"}], "after_files": [{"content": "#  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 decimal\nimport re\nfrom collections import namedtuple\nfrom typing import Pattern\n\n\ndef _starmatch_to_regex(pattern: str) -> Pattern:\n    \"\"\"\n    This is a duplicate of starmatch_to_regex() in utils/__init__.py\n\n    Duplication to avoid circular imports\n    \"\"\"\n    options = re.DOTALL\n    # check if we are case-sensitive\n    if pattern.startswith(\"(?-i)\"):\n        pattern = pattern[5:]\n    else:\n        options |= re.IGNORECASE\n    i, n = 0, len(pattern)\n    res = []\n    while i < n:\n        c = pattern[i]\n        i = i + 1\n        if c == \"*\":\n            res.append(\".*\")\n        else:\n            res.append(re.escape(c))\n    return re.compile(r\"(?:%s)\\Z\" % \"\".join(res), options)\n\n\nEVENTS_API_PATH = \"intake/v2/events\"\nAGENT_CONFIG_PATH = \"config/v1/agents\"\nSERVER_INFO_PATH = \"\"\n\nTRACE_CONTEXT_VERSION = 0\nTRACEPARENT_HEADER_NAME = \"traceparent\"\nTRACEPARENT_LEGACY_HEADER_NAME = \"elastic-apm-traceparent\"\nTRACEPARENT_BINARY_HEADER_NAME = \"elasticapmtraceparent\"\nTRACESTATE_HEADER_NAME = \"tracestate\"\n\nTIMESTAMP_FORMAT = \"%Y-%m-%dT%H:%M:%S.%fZ\"\n\nKEYWORD_MAX_LENGTH = 1024\nLONG_FIELD_MAX_LENGTH = 10000\n\nHTTP_WITH_BODY = {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n\nMASK = \"[REDACTED]\"\n\nEXCEPTION_CHAIN_MAX_DEPTH = 50\n\nERROR = \"error\"\nTRANSACTION = \"transaction\"\nSPAN = \"span\"\nMETRICSET = \"metricset\"\n\nLABEL_RE = re.compile('[.*\"]')\n\nHARDCODED_PROCESSORS = [\"elasticapm.processors.add_context_lines_to_frames\"]\n\nBASE_SANITIZE_FIELD_NAMES_UNPROCESSED = [\n    \"password\",\n    \"passwd\",\n    \"pwd\",\n    \"secret\",\n    \"*key\",\n    \"*token*\",\n    \"*session*\",\n    \"*credit*\",\n    \"*card*\",\n    \"*auth*\",\n    \"*principal*\",\n    \"set-cookie\",\n]\n\nBASE_SANITIZE_FIELD_NAMES = [_starmatch_to_regex(x) for x in BASE_SANITIZE_FIELD_NAMES_UNPROCESSED]\n\nOUTCOME = namedtuple(\"OUTCOME\", [\"SUCCESS\", \"FAILURE\", \"UNKNOWN\"])(\n    SUCCESS=\"success\", FAILURE=\"failure\", UNKNOWN=\"unknown\"\n)\n\nLABEL_TYPES = (bool, int, float, decimal.Decimal)\n\nTRACESTATE = namedtuple(\"TRACESTATE\", [\"SAMPLE_RATE\"])(SAMPLE_RATE=\"s\")\nTRACE_CONTINUATION_STRATEGY = namedtuple(\"TRACE_CONTINUATION_STRATEGY\", [\"CONTINUE\", \"RESTART\", \"RESTART_EXTERNAL\"])(\n    CONTINUE=\"continue\",\n    RESTART=\"restart\",\n    RESTART_EXTERNAL=\"restart_external\",\n)\n", "path": "elasticapm/conf/constants.py"}, {"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2012, the Sentry Team, see AUTHORS for more details\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\nimport base64\nimport os\nimport re\nimport urllib.parse\nfrom functools import partial\nfrom types import FunctionType\nfrom typing import Pattern\n\nfrom elasticapm.conf import constants\nfrom elasticapm.utils import encoding\n\ntry:\n    from functools import partialmethod\n\n    partial_types = (partial, partialmethod)\nexcept ImportError:\n    # Python 2\n    partial_types = (partial,)\n\n\ndefault_ports = {\"https\": 443, \"http\": 80, \"postgresql\": 5432, \"mysql\": 3306, \"mssql\": 1433}\n\n\ndef varmap(func, var, context=None, name=None, **kwargs):\n    \"\"\"\n    Executes ``func(key_name, value)`` on all values,\n    recursively discovering dict and list scoped\n    values.\n    \"\"\"\n    if context is None:\n        context = set()\n    objid = id(var)\n    if objid in context:\n        return func(name, \"<...>\", **kwargs)\n    context.add(objid)\n\n    # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values\n    ret = func(name, var, **kwargs)\n    if isinstance(ret, dict):\n        # iterate over a copy of the dictionary to avoid \"dictionary changed size during iteration\" issues\n        ret = dict((k, varmap(func, v, context, k, **kwargs)) for k, v in ret.copy().items())\n    elif isinstance(ret, (list, tuple)):\n        # Apply func() before recursion, so that `shorten()` doesn't have to iterate over all the trimmed values\n        ret = [varmap(func, f, context, name, **kwargs) for f in ret]\n    context.remove(objid)\n    return ret\n\n\ndef get_name_from_func(func: FunctionType) -> str:\n    # partials don't have `__module__` or `__name__`, so we use the values from the \"inner\" function\n    if isinstance(func, partial_types):\n        return \"partial({})\".format(get_name_from_func(func.func))\n    elif hasattr(func, \"_partialmethod\") and hasattr(func._partialmethod, \"func\"):\n        return \"partial({})\".format(get_name_from_func(func._partialmethod.func))\n\n    module = func.__module__\n\n    if hasattr(func, \"view_class\"):\n        view_name = func.view_class.__name__\n    elif hasattr(func, \"__name__\"):\n        view_name = func.__name__\n    else:  # Fall back if there's no __name__\n        view_name = func.__class__.__name__\n\n    return \"{0}.{1}\".format(module, view_name)\n\n\ndef build_name_with_http_method_prefix(name, request):\n    return \" \".join((request.method, name)) if name else name\n\n\ndef is_master_process() -> bool:\n    # currently only recognizes uwsgi master process\n    try:\n        import uwsgi\n\n        return os.getpid() == uwsgi.masterpid()\n    except ImportError:\n        return False\n\n\ndef get_url_dict(url: str) -> dict:\n    parse_result = urllib.parse.urlparse(url)\n\n    url_dict = {\n        \"full\": encoding.keyword_field(url),\n        \"protocol\": parse_result.scheme + \":\",\n        \"hostname\": encoding.keyword_field(parse_result.hostname),\n        \"pathname\": encoding.keyword_field(parse_result.path),\n    }\n\n    port = None if parse_result.port is None else str(parse_result.port)\n\n    if port:\n        url_dict[\"port\"] = port\n    if parse_result.query:\n        url_dict[\"search\"] = encoding.keyword_field(\"?\" + parse_result.query)\n    return url_dict\n\n\ndef sanitize_url(url: str) -> str:\n    if \"@\" not in url:\n        return url\n    parts = urllib.parse.urlparse(url)\n    return url.replace(\"%s:%s\" % (parts.username, parts.password), \"%s:%s\" % (parts.username, constants.MASK))\n\n\ndef get_host_from_url(url: str) -> str:\n    parsed_url = urllib.parse.urlparse(url)\n    host = parsed_url.hostname or \" \"\n\n    if parsed_url.port and default_ports.get(parsed_url.scheme) != parsed_url.port:\n        host += \":\" + str(parsed_url.port)\n\n    return host\n\n\ndef url_to_destination_resource(url: str) -> str:\n    parts = urllib.parse.urlsplit(url)\n    hostname = parts.hostname if parts.hostname else \"\"\n    # preserve brackets for IPv6 URLs\n    if \"://[\" in url:\n        hostname = \"[%s]\" % hostname\n    try:\n        port = parts.port\n    except ValueError:\n        # Malformed port, just use None rather than raising an exception\n        port = None\n    default_port = default_ports.get(parts.scheme, None)\n    name = \"%s://%s\" % (parts.scheme, hostname)\n    resource = hostname\n    if not port and parts.scheme in default_ports:\n        port = default_ports[parts.scheme]\n    if port:\n        if port != default_port:\n            name += \":%d\" % port\n        resource += \":%d\" % port\n    return resource\n\n\ndef read_pem_file(file_obj) -> bytes:\n    cert = b\"\"\n    for line in file_obj:\n        if line.startswith(b\"-----BEGIN CERTIFICATE-----\"):\n            break\n    # scan until we find the first END CERTIFICATE marker\n    for line in file_obj:\n        if line.startswith(b\"-----END CERTIFICATE-----\"):\n            break\n        cert += line.strip()\n    return base64.b64decode(cert)\n\n\ndef starmatch_to_regex(pattern: str) -> Pattern:\n    options = re.DOTALL\n    # check if we are case-sensitive\n    if pattern.startswith(\"(?-i)\"):\n        pattern = pattern[5:]\n    else:\n        options |= re.IGNORECASE\n    i, n = 0, len(pattern)\n    res = []\n    while i < n:\n        c = pattern[i]\n        i = i + 1\n        if c == \"*\":\n            res.append(\".*\")\n        else:\n            res.append(re.escape(c))\n    return re.compile(r\"(?:%s)\\Z\" % \"\".join(res), options)\n\n\ndef nested_key(d: dict, *args):\n    \"\"\"\n    Traverses a dictionary for nested keys. Returns `None` if the at any point\n    in the traversal a key cannot be found.\n\n    Example:\n\n        >>> from elasticapm.utils import nested_key\n        >>> d = {\"a\": {\"b\": {\"c\": 0}}}\n        >>> nested_key(d, \"a\", \"b\", \"c\")\n        0\n        >>> nested_key(d, \"a\", \"b\", \"d\")\n        None\n    \"\"\"\n    for arg in args:\n        try:\n            d = d[arg]\n        except (TypeError, KeyError):\n            d = None\n            break\n    return d\n", "path": "elasticapm/utils/__init__.py"}]}