prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add more features for adding HTTP request / response headers to spans.
  I already have https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1172 open for this, and I'll be breaking it in to smaller pieces at @lzchen 's request.
  
  **Is your feature request related to a problem?**
  Currently, you can only provide a list of full HTTP request / response header names to be added to the span.
  
  There is also no capacity for header value redaction.
  
  **Describe the solution you'd like**
  It would be nice to be able to specify a regex or "all" to get all headers.
  
  Header value redaction is also a must-have for us.
  
  **Describe alternatives you've considered**
  I considered doing this in my application, but it makes more sense to add it here.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py`
  Content:
  ```
  1 # Copyright The OpenTelemetry Authors
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 from os import environ
  16 from re import compile as re_compile
  17 from re import search
  18 from typing import Iterable, List
  19 from urllib.parse import urlparse, urlunparse
  20 
  21 from opentelemetry.semconv.trace import SpanAttributes
  22 
  23 OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = (
  24     "OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST"
  25 )
  26 OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE = (
  27     "OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE"
  28 )
  29 
  30 # List of recommended metrics attributes
  31 _duration_attrs = {
  32     SpanAttributes.HTTP_METHOD,
  33     SpanAttributes.HTTP_HOST,
  34     SpanAttributes.HTTP_SCHEME,
  35     SpanAttributes.HTTP_STATUS_CODE,
  36     SpanAttributes.HTTP_FLAVOR,
  37     SpanAttributes.HTTP_SERVER_NAME,
  38     SpanAttributes.NET_HOST_NAME,
  39     SpanAttributes.NET_HOST_PORT,
  40 }
  41 
  42 _active_requests_count_attrs = {
  43     SpanAttributes.HTTP_METHOD,
  44     SpanAttributes.HTTP_HOST,
  45     SpanAttributes.HTTP_SCHEME,
  46     SpanAttributes.HTTP_FLAVOR,
  47     SpanAttributes.HTTP_SERVER_NAME,
  48 }
  49 
  50 
  51 class ExcludeList:
  52     """Class to exclude certain paths (given as a list of regexes) from tracing requests"""
  53 
  54     def __init__(self, excluded_urls: Iterable[str]):
  55         self._excluded_urls = excluded_urls
  56         if self._excluded_urls:
  57             self._regex = re_compile("|".join(excluded_urls))
  58 
  59     def url_disabled(self, url: str) -> bool:
  60         return bool(self._excluded_urls and search(self._regex, url))
  61 
  62 
  63 _root = r"OTEL_PYTHON_{}"
  64 
  65 
  66 def get_traced_request_attrs(instrumentation):
  67     traced_request_attrs = environ.get(
  68         _root.format(f"{instrumentation}_TRACED_REQUEST_ATTRS"), []
  69     )
  70 
  71     if traced_request_attrs:
  72         traced_request_attrs = [
  73             traced_request_attr.strip()
  74             for traced_request_attr in traced_request_attrs.split(",")
  75         ]
  76 
  77     return traced_request_attrs
  78 
  79 
  80 def get_excluded_urls(instrumentation: str) -> ExcludeList:
  81     # Get instrumentation-specific excluded URLs. If not set, retrieve them
  82     # from generic variable.
  83     excluded_urls = environ.get(
  84         _root.format(f"{instrumentation}_EXCLUDED_URLS"),
  85         environ.get(_root.format("EXCLUDED_URLS"), ""),
  86     )
  87 
  88     return parse_excluded_urls(excluded_urls)
  89 
  90 
  91 def parse_excluded_urls(excluded_urls: str) -> ExcludeList:
  92     """
  93     Small helper to put an arbitrary url list inside of ExcludeList
  94     """
  95     if excluded_urls:
  96         excluded_url_list = [
  97             excluded_url.strip() for excluded_url in excluded_urls.split(",")
  98         ]
  99     else:
  100         excluded_url_list = []
  101 
  102     return ExcludeList(excluded_url_list)
  103 
  104 
  105 def remove_url_credentials(url: str) -> str:
  106     """Given a string url, remove the username and password only if it is a valid url"""
  107 
  108     try:
  109         parsed = urlparse(url)
  110         if all([parsed.scheme, parsed.netloc]):  # checks for valid url
  111             parsed_url = urlparse(url)
  112             netloc = (
  113                 (":".join(((parsed_url.hostname or ""), str(parsed_url.port))))
  114                 if parsed_url.port
  115                 else (parsed_url.hostname or "")
  116             )
  117             return urlunparse(
  118                 (
  119                     parsed_url.scheme,
  120                     netloc,
  121                     parsed_url.path,
  122                     parsed_url.params,
  123                     parsed_url.query,
  124                     parsed_url.fragment,
  125                 )
  126             )
  127     except ValueError:  # an unparsable url was passed
  128         pass
  129     return url
  130 
  131 
  132 def normalise_request_header_name(header: str) -> str:
  133     key = header.lower().replace("-", "_")
  134     return f"http.request.header.{key}"
  135 
  136 
  137 def normalise_response_header_name(header: str) -> str:
  138     key = header.lower().replace("-", "_")
  139     return f"http.response.header.{key}"
  140 
  141 
  142 def get_custom_headers(env_var: str) -> List[str]:
  143     custom_headers = environ.get(env_var, [])
  144     if custom_headers:
  145         custom_headers = [
  146             custom_headers.strip()
  147             for custom_headers in custom_headers.split(",")
  148         ]
  149     return custom_headers
  150 
  151 
  152 def _parse_active_request_count_attrs(req_attrs):
  153     active_requests_count_attrs = {
  154         key: req_attrs[key]
  155         for key in _active_requests_count_attrs.intersection(req_attrs.keys())
  156     }
  157     return active_requests_count_attrs
  158 
  159 
  160 def _parse_duration_attrs(req_attrs):
  161     duration_attrs = {
  162         key: req_attrs[key]
  163         for key in _duration_attrs.intersection(req_attrs.keys())
  164     }
  165     return duration_attrs
  166
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
  --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
  +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py
  @@ -13,6 +13,7 @@
   # limitations under the License.
   
   from os import environ
  +from re import IGNORECASE as RE_IGNORECASE
   from re import compile as re_compile
   from re import search
   from typing import Iterable, List
  @@ -20,6 +21,9 @@
   
   from opentelemetry.semconv.trace import SpanAttributes
   
  +OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS = (
  +    "OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS"
  +)
   OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = (
       "OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST"
   )
  @@ -60,6 +64,22 @@
           return bool(self._excluded_urls and search(self._regex, url))
   
   
  +class SanitizeValue:
  +    """Class to sanitize (remove sensitive data from) certain headers (given as a list of regexes)"""
  +
  +    def __init__(self, sanitized_fields: Iterable[str]):
  +        self._sanitized_fields = sanitized_fields
  +        if self._sanitized_fields:
  +            self._regex = re_compile("|".join(sanitized_fields), RE_IGNORECASE)
  +
  +    def sanitize_header_value(self, header: str, value: str) -> str:
  +        return (
  +            "[REDACTED]"
  +            if (self._sanitized_fields and search(self._regex, header))
  +            else value
  +        )
  +
  +
   _root = r"OTEL_PYTHON_{}"
   
   
  @@ -90,7 +110,7 @@
   
   def parse_excluded_urls(excluded_urls: str) -> ExcludeList:
       """
  -    Small helper to put an arbitrary url list inside of ExcludeList
  +    Small helper to put an arbitrary url list inside an ExcludeList
       """
       if excluded_urls:
           excluded_url_list = [
  
verification_info: {"golden_diff": "diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py\n--- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py\n+++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py\n@@ -13,6 +13,7 @@\n # limitations under the License.\n \n from os import environ\n+from re import IGNORECASE as RE_IGNORECASE\n from re import compile as re_compile\n from re import search\n from typing import Iterable, List\n@@ -20,6 +21,9 @@\n \n from opentelemetry.semconv.trace import SpanAttributes\n \n+OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS = (\n+    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS\"\n+)\n OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = (\n     \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST\"\n )\n@@ -60,6 +64,22 @@\n         return bool(self._excluded_urls and search(self._regex, url))\n \n \n+class SanitizeValue:\n+    \"\"\"Class to sanitize (remove sensitive data from) certain headers (given as a list of regexes)\"\"\"\n+\n+    def __init__(self, sanitized_fields: Iterable[str]):\n+        self._sanitized_fields = sanitized_fields\n+        if self._sanitized_fields:\n+            self._regex = re_compile(\"|\".join(sanitized_fields), RE_IGNORECASE)\n+\n+    def sanitize_header_value(self, header: str, value: str) -> str:\n+        return (\n+            \"[REDACTED]\"\n+            if (self._sanitized_fields and search(self._regex, header))\n+            else value\n+        )\n+\n+\n _root = r\"OTEL_PYTHON_{}\"\n \n \n@@ -90,7 +110,7 @@\n \n def parse_excluded_urls(excluded_urls: str) -> ExcludeList:\n     \"\"\"\n-    Small helper to put an arbitrary url list inside of ExcludeList\n+    Small helper to put an arbitrary url list inside an ExcludeList\n     \"\"\"\n     if excluded_urls:\n         excluded_url_list = [\n", "issue": "Add more features for adding HTTP request / response headers to spans.\nI already have https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1172 open for this, and I'll be breaking it in to smaller pieces at @lzchen 's request.\r\n\r\n**Is your feature request related to a problem?**\r\nCurrently, you can only provide a list of full HTTP request / response header names to be added to the span.\r\n\r\nThere is also no capacity for header value redaction.\r\n\r\n**Describe the solution you'd like**\r\nIt would be nice to be able to specify a regex or \"all\" to get all headers.\r\n\r\nHeader value redaction is also a must-have for us.\r\n\r\n**Describe alternatives you've considered**\r\nI considered doing this in my application, but it makes more sense to add it here.\r\n\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom os import environ\nfrom re import compile as re_compile\nfrom re import search\nfrom typing import Iterable, List\nfrom urllib.parse import urlparse, urlunparse\n\nfrom opentelemetry.semconv.trace import SpanAttributes\n\nOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = (\n    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST\"\n)\nOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE = (\n    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE\"\n)\n\n# List of recommended metrics attributes\n_duration_attrs = {\n    SpanAttributes.HTTP_METHOD,\n    SpanAttributes.HTTP_HOST,\n    SpanAttributes.HTTP_SCHEME,\n    SpanAttributes.HTTP_STATUS_CODE,\n    SpanAttributes.HTTP_FLAVOR,\n    SpanAttributes.HTTP_SERVER_NAME,\n    SpanAttributes.NET_HOST_NAME,\n    SpanAttributes.NET_HOST_PORT,\n}\n\n_active_requests_count_attrs = {\n    SpanAttributes.HTTP_METHOD,\n    SpanAttributes.HTTP_HOST,\n    SpanAttributes.HTTP_SCHEME,\n    SpanAttributes.HTTP_FLAVOR,\n    SpanAttributes.HTTP_SERVER_NAME,\n}\n\n\nclass ExcludeList:\n    \"\"\"Class to exclude certain paths (given as a list of regexes) from tracing requests\"\"\"\n\n    def __init__(self, excluded_urls: Iterable[str]):\n        self._excluded_urls = excluded_urls\n        if self._excluded_urls:\n            self._regex = re_compile(\"|\".join(excluded_urls))\n\n    def url_disabled(self, url: str) -> bool:\n        return bool(self._excluded_urls and search(self._regex, url))\n\n\n_root = r\"OTEL_PYTHON_{}\"\n\n\ndef get_traced_request_attrs(instrumentation):\n    traced_request_attrs = environ.get(\n        _root.format(f\"{instrumentation}_TRACED_REQUEST_ATTRS\"), []\n    )\n\n    if traced_request_attrs:\n        traced_request_attrs = [\n            traced_request_attr.strip()\n            for traced_request_attr in traced_request_attrs.split(\",\")\n        ]\n\n    return traced_request_attrs\n\n\ndef get_excluded_urls(instrumentation: str) -> ExcludeList:\n    # Get instrumentation-specific excluded URLs. If not set, retrieve them\n    # from generic variable.\n    excluded_urls = environ.get(\n        _root.format(f\"{instrumentation}_EXCLUDED_URLS\"),\n        environ.get(_root.format(\"EXCLUDED_URLS\"), \"\"),\n    )\n\n    return parse_excluded_urls(excluded_urls)\n\n\ndef parse_excluded_urls(excluded_urls: str) -> ExcludeList:\n    \"\"\"\n    Small helper to put an arbitrary url list inside of ExcludeList\n    \"\"\"\n    if excluded_urls:\n        excluded_url_list = [\n            excluded_url.strip() for excluded_url in excluded_urls.split(\",\")\n        ]\n    else:\n        excluded_url_list = []\n\n    return ExcludeList(excluded_url_list)\n\n\ndef remove_url_credentials(url: str) -> str:\n    \"\"\"Given a string url, remove the username and password only if it is a valid url\"\"\"\n\n    try:\n        parsed = urlparse(url)\n        if all([parsed.scheme, parsed.netloc]):  # checks for valid url\n            parsed_url = urlparse(url)\n            netloc = (\n                (\":\".join(((parsed_url.hostname or \"\"), str(parsed_url.port))))\n                if parsed_url.port\n                else (parsed_url.hostname or \"\")\n            )\n            return urlunparse(\n                (\n                    parsed_url.scheme,\n                    netloc,\n                    parsed_url.path,\n                    parsed_url.params,\n                    parsed_url.query,\n                    parsed_url.fragment,\n                )\n            )\n    except ValueError:  # an unparsable url was passed\n        pass\n    return url\n\n\ndef normalise_request_header_name(header: str) -> str:\n    key = header.lower().replace(\"-\", \"_\")\n    return f\"http.request.header.{key}\"\n\n\ndef normalise_response_header_name(header: str) -> str:\n    key = header.lower().replace(\"-\", \"_\")\n    return f\"http.response.header.{key}\"\n\n\ndef get_custom_headers(env_var: str) -> List[str]:\n    custom_headers = environ.get(env_var, [])\n    if custom_headers:\n        custom_headers = [\n            custom_headers.strip()\n            for custom_headers in custom_headers.split(\",\")\n        ]\n    return custom_headers\n\n\ndef _parse_active_request_count_attrs(req_attrs):\n    active_requests_count_attrs = {\n        key: req_attrs[key]\n        for key in _active_requests_count_attrs.intersection(req_attrs.keys())\n    }\n    return active_requests_count_attrs\n\n\ndef _parse_duration_attrs(req_attrs):\n    duration_attrs = {\n        key: req_attrs[key]\n        for key in _duration_attrs.intersection(req_attrs.keys())\n    }\n    return duration_attrs\n", "path": "util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom os import environ\nfrom re import IGNORECASE as RE_IGNORECASE\nfrom re import compile as re_compile\nfrom re import search\nfrom typing import Iterable, List\nfrom urllib.parse import urlparse, urlunparse\n\nfrom opentelemetry.semconv.trace import SpanAttributes\n\nOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS = (\n    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS\"\n)\nOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = (\n    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST\"\n)\nOTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE = (\n    \"OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE\"\n)\n\n# List of recommended metrics attributes\n_duration_attrs = {\n    SpanAttributes.HTTP_METHOD,\n    SpanAttributes.HTTP_HOST,\n    SpanAttributes.HTTP_SCHEME,\n    SpanAttributes.HTTP_STATUS_CODE,\n    SpanAttributes.HTTP_FLAVOR,\n    SpanAttributes.HTTP_SERVER_NAME,\n    SpanAttributes.NET_HOST_NAME,\n    SpanAttributes.NET_HOST_PORT,\n}\n\n_active_requests_count_attrs = {\n    SpanAttributes.HTTP_METHOD,\n    SpanAttributes.HTTP_HOST,\n    SpanAttributes.HTTP_SCHEME,\n    SpanAttributes.HTTP_FLAVOR,\n    SpanAttributes.HTTP_SERVER_NAME,\n}\n\n\nclass ExcludeList:\n    \"\"\"Class to exclude certain paths (given as a list of regexes) from tracing requests\"\"\"\n\n    def __init__(self, excluded_urls: Iterable[str]):\n        self._excluded_urls = excluded_urls\n        if self._excluded_urls:\n            self._regex = re_compile(\"|\".join(excluded_urls))\n\n    def url_disabled(self, url: str) -> bool:\n        return bool(self._excluded_urls and search(self._regex, url))\n\n\nclass SanitizeValue:\n    \"\"\"Class to sanitize (remove sensitive data from) certain headers (given as a list of regexes)\"\"\"\n\n    def __init__(self, sanitized_fields: Iterable[str]):\n        self._sanitized_fields = sanitized_fields\n        if self._sanitized_fields:\n            self._regex = re_compile(\"|\".join(sanitized_fields), RE_IGNORECASE)\n\n    def sanitize_header_value(self, header: str, value: str) -> str:\n        return (\n            \"[REDACTED]\"\n            if (self._sanitized_fields and search(self._regex, header))\n            else value\n        )\n\n\n_root = r\"OTEL_PYTHON_{}\"\n\n\ndef get_traced_request_attrs(instrumentation):\n    traced_request_attrs = environ.get(\n        _root.format(f\"{instrumentation}_TRACED_REQUEST_ATTRS\"), []\n    )\n\n    if traced_request_attrs:\n        traced_request_attrs = [\n            traced_request_attr.strip()\n            for traced_request_attr in traced_request_attrs.split(\",\")\n        ]\n\n    return traced_request_attrs\n\n\ndef get_excluded_urls(instrumentation: str) -> ExcludeList:\n    # Get instrumentation-specific excluded URLs. If not set, retrieve them\n    # from generic variable.\n    excluded_urls = environ.get(\n        _root.format(f\"{instrumentation}_EXCLUDED_URLS\"),\n        environ.get(_root.format(\"EXCLUDED_URLS\"), \"\"),\n    )\n\n    return parse_excluded_urls(excluded_urls)\n\n\ndef parse_excluded_urls(excluded_urls: str) -> ExcludeList:\n    \"\"\"\n    Small helper to put an arbitrary url list inside an ExcludeList\n    \"\"\"\n    if excluded_urls:\n        excluded_url_list = [\n            excluded_url.strip() for excluded_url in excluded_urls.split(\",\")\n        ]\n    else:\n        excluded_url_list = []\n\n    return ExcludeList(excluded_url_list)\n\n\ndef remove_url_credentials(url: str) -> str:\n    \"\"\"Given a string url, remove the username and password only if it is a valid url\"\"\"\n\n    try:\n        parsed = urlparse(url)\n        if all([parsed.scheme, parsed.netloc]):  # checks for valid url\n            parsed_url = urlparse(url)\n            netloc = (\n                (\":\".join(((parsed_url.hostname or \"\"), str(parsed_url.port))))\n                if parsed_url.port\n                else (parsed_url.hostname or \"\")\n            )\n            return urlunparse(\n                (\n                    parsed_url.scheme,\n                    netloc,\n                    parsed_url.path,\n                    parsed_url.params,\n                    parsed_url.query,\n                    parsed_url.fragment,\n                )\n            )\n    except ValueError:  # an unparsable url was passed\n        pass\n    return url\n\n\ndef normalise_request_header_name(header: str) -> str:\n    key = header.lower().replace(\"-\", \"_\")\n    return f\"http.request.header.{key}\"\n\n\ndef normalise_response_header_name(header: str) -> str:\n    key = header.lower().replace(\"-\", \"_\")\n    return f\"http.response.header.{key}\"\n\n\ndef get_custom_headers(env_var: str) -> List[str]:\n    custom_headers = environ.get(env_var, [])\n    if custom_headers:\n        custom_headers = [\n            custom_headers.strip()\n            for custom_headers in custom_headers.split(\",\")\n        ]\n    return custom_headers\n\n\ndef _parse_active_request_count_attrs(req_attrs):\n    active_requests_count_attrs = {\n        key: req_attrs[key]\n        for key in _active_requests_count_attrs.intersection(req_attrs.keys())\n    }\n    return active_requests_count_attrs\n\n\ndef _parse_duration_attrs(req_attrs):\n    duration_attrs = {\n        key: req_attrs[key]\n        for key in _duration_attrs.intersection(req_attrs.keys())\n    }\n    return duration_attrs\n", "path": "util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py"}]}