Issue 1:
Requests does not accept headers that are subclasses of string or byte.

They are required i.e. to obfuscate secrets in certain environments, so this is also a security related issue.

The issue was introduced by e36f345 at June this year by @nateprewitt

A possible fix is given in this PR #6230

Expected Result

Headers are used as they are string/byte.

Actual Result

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/tmp/venv/lib64/python3.10/site-packages/requests/api.py", line 73, in get
return request("get", url, params=params, **kwargs)
File "/tmp/venv/lib64/python3.10/site-packages/requests/api.py", line 59, in request
return session.request(method=method, url=url, **kwargs)
File "/tmp/venv/lib64/python3.10/site-packages/requests/sessions.py", line 573, in request
prep = self.prepare_request(req)
File "/tmp/venv/lib64/python3.10/site-packages/requests/sessions.py", line 484, in prepare_request
p.prepare(
File "/tmp/venv/lib64/python3.10/site-packages/requests/models.py", line 369, in prepare
self.prepare_headers(headers)
File "/tmp/venv/lib64/python3.10/site-packages/requests/models.py", line 491, in prepare_headers
check_header_validity(header)
File "/tmp/venv/lib64/python3.10/site-packages/requests/utils.py", line 1037, in check_header_validity
raise InvalidHeader(
requests.exceptions.InvalidHeader: Header part (Hidden) from {'token': Hidden} must be of type str or bytes, not <class '__main__.Secret'>

Reproduction Steps

import requests

class Secret(str):
def __repr__(self):
return "Hidden"

requests.get("https://google.com", headers={"token": Secret("Confidential")})

System Information

$ python -m requests.help

{
"chardet": {
"version": null
},
"charset_normalizer": {
"version": "2.1.1"
},
"cryptography": {
"version": ""
},
"idna": {
"version": "3.3"
},
"implementation": {
"name": "CPython",
"version": "3.10.6"
},
"platform": {
"release": "5.19.6-100.fc35.x86_64",
"system": "Linux"
},
"pyOpenSSL": {
"openssl_version": "",
"version": null
},
"requests": {
"version": "2.28.1"
},
"system_ssl": {
"version": "1010111f"
},
"urllib3": {
"version": "1.26.12"
},
"using_charset_normalizer": true,
"using_pyopenssl": false
}

Issue 2:
When using string enums as key values for the headers dict parameter with requests.get() function in v2.28.0 it raises an InvalidHeader error but using previous version v2.27.1 works well without any error. It seems there was an unexpected breaking change with that release.

Expected Result

Dict keys for the headers parameter when using requests.get() are still valid if a string enum is used.

Actual Result

Instead it raises an InvalidHeader error stating that:

Header must be of type str or bytes, not enum.

Traceback (most recent call last):
File "/home/yair/PycharmProjects/tests/requests_error.py", line 9, in <module>
requests.get("http://URL", headers={CustomEnum.TRACE_ID: "90e85293-afd1-4b48-adf0-fa6daf02359e"})
File "/home/yair/.local/lib/python3.8/site-packages/requests/api.py", line 73, in get
return request("get", url, params=params, **kwargs)
File "/home/yair/.local/lib/python3.8/site-packages/requests/api.py", line 59, in request
return session.request(method=method, url=url, **kwargs)
File "/home/yair/.local/lib/python3.8/site-packages/requests/sessions.py", line 573, in request
prep = self.prepare_request(req)
File "/home/yair/.local/lib/python3.8/site-packages/requests/sessions.py", line 484, in prepare_request
p.prepare(
File "/home/yair/.local/lib/python3.8/site-packages/requests/models.py", line 369, in prepare
self.prepare_headers(headers)
File "/home/yair/.local/lib/python3.8/site-packages/requests/models.py", line 491, in prepare_headers
check_header_validity(header)
File "/home/yair/.local/lib/python3.8/site-packages/requests/utils.py", line 1037, in check_header_validity
raise InvalidHeader(
requests.exceptions.InvalidHeader: Header part (<CustomEnum.TRACE_ID: 'X-B3-TraceId'>) from {<CustomEnum.TRACE_ID: 'X-B3-TraceId'>: '90e85293-afd1-4b48-adf0-fa6daf02359e'} must be of type str or bytes, not <enum 'CustomEnum'>

Reproduction Steps

from enum import Enum
import requests

class CustomEnum(str, Enum):
TRACE_ID = "X-B3-TraceId"

requests.get("http://URL", headers={CustomEnum.TRACE_ID: "90e85293-afd1-4b48-adf0-fa6daf02359e"})

System Information

$ python -m requests.help

{
"chardet": {
"version": "3.0.4"
},
"charset_normalizer": {
"version": "2.0.4"
},
"cryptography": {
"version": "2.8"
},
"idna": {
"version": "3.2"
},
"implementation": {
"name": "CPython",
"version": "3.8.10"
},
"platform": {
"release": "5.13.0-48-generic",
"system": "Linux"
},
"pyOpenSSL": {
"openssl_version": "1010106f",
"version": "19.0.0"
},
"requests": {
"version": "2.28.0"
},
"system_ssl": {
"version": "1010106f"
},
"urllib3": {
"version": "1.26.6"
},
"using_charset_normalizer": false,
"using_pyopenssl": true
}