Repository: streamlink/streamlink

Issue:
HTTP NO SSL Verify broken
### Checklist

- [X] This is a bug report and not [a different kind of issue](https://github.com/streamlink/streamlink/issues/new/choose)
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed bug reports](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22bug%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Collaboration

- [X] [I will provide feedback should a pull request be opened with a fix for the problem](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#pull-request-feedback)

### Streamlink version

[cli][info] Your Streamlink version (6.10.0) is up to date!

### Description

Recently, I noticed that streams with SSL enabled do not play on streamlink. 

By default, I get such message : 
``
[cli][info] Found matching plugin hls for URL https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
error: Unable to open URL: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (HTTPSConnectionPool(host='s2.callofliberty.fr', port=443): Max retries exceeded with url: /coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)'))))
``

With SSL Verify disabled : 
``
streamlink --http-no-ssl-verify "https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0"
[cli][info] Found matching plugin hls for URL https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
error: Unable to open URL: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (Cannot set verify_mode to CERT_NONE when check_hostname is enabled.)
``

### Debug log

```text
SSL Verify Enabled

streamlink -l debug "https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0"
[session][debug] Loading plugin: hls
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.12.2
[cli][debug] OpenSSL:    OpenSSL 3.0.13 30 Jan 2024
[cli][debug] Streamlink: 6.10.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2024.2.2
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 5.1.0
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.25.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.10.0
[cli][debug]  urllib3: 2.2.1
[cli][debug]  websocket-client: 1.7.0
[cli][debug]  exceptiongroup: 1.2.0
[cli][debug] Arguments:
[cli][debug]  url=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[cli][debug]  --loglevel=debug
[cli][debug]  --title=0
[cli][debug]  --hls-audio-select=['*']
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Python312\Lib\site-packages\ffmpeg\ffmpeg.exe
[cli][debug]  --http-header=[('User-Agent', 'Mozilla/5.0')]
[cli][debug]  --http-disable-dh=True
[cli][info] Found matching plugin hls for URL https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[plugins.hls][debug] URL=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0; params={}
[utils.l10n][debug] Language code: en_US
error: Unable to open URL: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (HTTPSConnectionPool(host='s2.callofliberty.fr', port=443): Max retries exceeded with url: /coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)'))))


SSL Verify Disabled

streamlink -l debug --http-no-ssl-verify "https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0"
[session][debug] Loading plugin: hls
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.12.2
[cli][debug] OpenSSL:    OpenSSL 3.0.13 30 Jan 2024
[cli][debug] Streamlink: 6.10.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2024.2.2
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 5.1.0
[cli][debug]  pycountry: 23.12.11
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.31.0
[cli][debug]  trio: 0.25.0
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.10.0
[cli][debug]  urllib3: 2.2.1
[cli][debug]  websocket-client: 1.7.0
[cli][debug]  exceptiongroup: 1.2.0
[cli][debug] Arguments:
[cli][debug]  url=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[cli][debug]  --loglevel=debug
[cli][debug]  --title=0
[cli][debug]  --hls-audio-select=['*']
[cli][debug]  --ffmpeg-ffmpeg=C:\Program Files\Python312\Lib\site-packages\ffmpeg\ffmpeg.exe
[cli][debug]  --http-header=[('User-Agent', 'Mozilla/5.0')]
[cli][debug]  --http-no-ssl-verify=False
[cli][debug]  --http-disable-dh=True
[cli][info] Found matching plugin hls for URL https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[plugins.hls][debug] URL=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0; params={}
[utils.l10n][debug] Language code: en_US
error: Unable to open URL: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (Cannot set verify_mode to CERT_NONE when check_hostname is enabled.)
```



Discussion:
The option is not broken and correctly does what it's supposed to, namely it sets the `HTTPSession.verify` attribute ([1](https://github.com/streamlink/streamlink/blob/6.10.0/src/streamlink/session/options.py#L455), [2](https://github.com/streamlink/streamlink/blob/6.10.0/src/streamlink/session/options.py#L411-L412), [3](https://github.com/streamlink/streamlink/blob/6.10.0/src/streamlink_cli/argparser.py#L1428), [4](https://github.com/streamlink/streamlink/blob/6.10.0/tests/session/test_options.py#L277-L292)), handled by `requests`, not by Streamlink:

> If verify is set to False, requests will accept any TLS certificate presented by the server, and will ignore hostname mismatches and/or expired certificates, which will make your application vulnerable to man-in-the-middle (MitM) attacks.

https://requests.readthedocs.io/en/latest/api/#requests.Session.verify

----

This is a server misconfiguration, as you can see here (hostname mismatches):

```
$ echo "" | openssl s_client -connect s2.callofliberty.fr:443 -showcerts 
Connecting to 80.15.29.36
CONNECTED(00000003)
depth=0 CN=www.callofliberty.fr
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN=www.callofliberty.fr
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 CN=www.callofliberty.fr
verify return:1
---
Certificate chain
 0 s:CN=www.callofliberty.fr
   i:C=US, O=Let's Encrypt, CN=R11
   a:PKEY: rsaEncryption, 3072 (bit); sigalg: RSA-SHA256
   v:NotBefore: Sep  4 16:56:15 2024 GMT; NotAfter: Dec  3 16:56:14 2024 GMT
-----BEGIN CERTIFICATE-----
MIIFjTCCBHWgAwIBAgISBEs/LVW3Rx9fVoKbOPDEFKj3MA0GCSqGSIb3DQEBCwUA
MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD
EwNSMTEwHhcNMjQwOTA0MTY1NjE1WhcNMjQxMjAzMTY1NjE0WjAfMR0wGwYDVQQD
ExR3d3cuY2FsbG9mbGliZXJ0eS5mcjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCC
AYoCggGBAKdAg2tPvqzxoZzXUkHOrzn0nekIgzEGnOQlTJRD/T0Sm+OKatqdtA0v
RstmpVMtmvrVBnzb+UQgSZOSG5W3KIXXIDwzAsIJ2pyUVYFwQPt0RDzRAOELdUoi
L93Cc6XNgO4LRSB4z7Uje8t7eJBF8W3FneaahePvKziKdCxnEuVCp9dH16c4UzDS
DkBFe/yQlYXYgHkXLWbTQkBm+RhZKBZw6SgZ2l3iZ3qtVYJRBea/+rf/b1goEiFn
e2Nx9kewZC7YO1e702kp15ONc7hiOUgwCa1FJCH0QzVsaGvYRDvwQG6C0odxOjWN
T/pPuLiCIHx3O9qrBJfHdsmOEoCQWEzURbKlKQydKPMkwBGsxt7r+NcSFsHJgfQ2
KaNsxqonY2bm4Ylo3+C/SIvhpBr6ci/N4XV/qgx5gKhV7gwvfMWcyLhyJOo7Bp/u
dCx1vxkahex1El3eoYhEn6T2QwGpMHghgTnKseHKZUXAxzzgFdQB8ptDexWlFewb
70tN3dlPcwIDAQABo4ICLTCCAikwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQD3KZn
r4i+pQTUpg1+HATYdzkcWTAfBgNVHSMEGDAWgBTFz0ak6vTDwHpslcQtsF6SLybj
uTBXBggrBgEFBQcBAQRLMEkwIgYIKwYBBQUHMAGGFmh0dHA6Ly9yMTEuby5sZW5j
ci5vcmcwIwYIKwYBBQUHMAKGF2h0dHA6Ly9yMTEuaS5sZW5jci5vcmcvMDQGA1Ud
EQQtMCuCE3MyLmNhbGxvZmxpYmVydHkuZnKCFHd3dy5jYWxsb2ZsaWJlcnR5LmZy
MBMGA1UdIAQMMAowCAYGZ4EMAQIBMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHUA
SLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGRviyy2gAABAMARjBE
AiBKWfWYt6PEnvNMRYy+31HjVbwA+KLpniEItw1f8h0glQIgdELZAgPTmszeEAz1
Cq++cCRahZMx5qgDk0oLyD8+X7IAdwA/F0tP1yJHWJQdZRyEvg0S7ZA3fx+FauvB
vyiF7PhkbgAAAZG+LLLVAAAEAwBIMEYCIQDpGrV8CH4IXKArR9L1zJvK53EDUMjG
S8sxUyNI7feZnQIhAI99PmKDiCPK7d1JaoslxujSPmxDc1T2t0edtLswFtGyMA0G
CSqGSIb3DQEBCwUAA4IBAQAKTe4tNO00AeAbzQGLk0IAJamL7uC1y+aEvNSIPYtF
zUw5R1vwlRgCfd3iDnAhmiMJPHv9MX0LiANCKpJ3ZtFwuusCwEeUkY+ei0My2xfT
BOvQjK9UrlA+B7HXfHMf4g2K0tVR8k1wnR9RBwIomE6tAx/m8FWhylXmvFgE4uT+
zMVQzE7sar9d5oO1U3WZcldATUnRcmVH5j0P1sHf25idI2/mIfVKBTUaqSp9bMM1
XiOxyunegDTDgnm7RTEGm3eSurJ6wmIxckZMVj4EirCE/i9Lj2hl2oF/ZXNXz/+6
B7o2bAv7kM9ESD3EDyXLqsvcJ5maEMMgMpYih5NjIC9P
-----END CERTIFICATE-----
---
Server certificate
subject=CN=www.callofliberty.fr
issuer=C=US, O=Let's Encrypt, CN=R11
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2109 bytes and written 407 bytes
Verification error: unable to verify the first certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 3072 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 21 (unable to verify the first certificate)
---
DONE
```

Setting `--http-no-ssl-verify` works, but the server response is `403`:
```
$ streamlink --no-config -l debug --http-no-ssl-verify https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[cli][debug] OS:         Linux-6.11.0-1-git-x86_64-with-glibc2.40
[cli][debug] Python:     3.12.6
[cli][debug] OpenSSL:    OpenSSL 3.3.2 3 Sep 2024
[cli][debug] Streamlink: 6.10.0+37.g91039f82
[cli][debug] Dependencies:
[cli][debug]  certifi: 2024.8.30
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 5.3.0
[cli][debug]  pycountry: 24.6.1
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.32.3
[cli][debug]  trio: 0.26.2
[cli][debug]  trio-websocket: 0.12.0.dev0
[cli][debug]  typing-extensions: 4.12.2
[cli][debug]  urllib3: 2.2.3
[cli][debug]  websocket-client: 1.8.0
[cli][debug] Arguments:
[cli][debug]  url=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[cli][debug]  --no-config=True
[cli][debug]  --loglevel=debug
[cli][debug]  --http-no-ssl-verify=False
[cli][info] Found matching plugin hls for URL https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0
[plugins.hls][debug] URL=https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0; params={}
[utils.l10n][debug] Language code: en_US
[warnings][insecurerequestwarning] Unverified HTTPS request is being made to host 's2.callofliberty.fr'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  /home/basti/venv/streamlink-312/lib/python3.12/site-packages/urllib3/connectionpool.py:1099
error: Unable to open URL: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0 (403 Client Error: Forbidden for url: https://s2.callofliberty.fr/coldtv/RTS1/index.m3u8?callofliberty=c4eb578121a1f142fb270fabe28ba3b2568c9b0)
```
@bastimeyer 
As you can see below, as an example HTTP NO SSL Verification switch gives me same messages on different channels. 
When I used python2, this switch always worked. Sometimes, disabling SSL Verification allows me to play stream which doesn't work otherwise so it would be good to fix this bug. Those streams play with enabled SSL but it's given as a proof that --http-no-ssl-verify doesn't apply. 

```
streamlink --http-no-ssl-verify "https://live-kto.akamaized.net/hls/live/2033284/KTO/master.m3u8" best
[cli][info] Found matching plugin hls for URL https://live-kto.akamaized.net/hls/live/2033284/KTO/master.m3u8
error: Unable to open URL: https://live-kto.akamaized.net/hls/live/2033284/KTO/master.m3u8 (Cannot set verify_mode to CERT_NONE when check_hostname is enabled.)
```

```
streamlink --http-no-ssl-verify "https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8" best
[cli][info] Found matching plugin hls for URL https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
error: Unable to open URL: https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8 (Cannot set verify_mode to CERT_NONE when check_hostname is enabled.)
```


Update your dependencies. Yours are outdated.

```
$ streamlink --http-no-ssl-verify "https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8" best
[cli][info] Found matching plugin hls for URL https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[warnings][insecurerequestwarning] Unverified HTTPS request is being made to host 'stream.mediawork.cz'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  /home/basti/venv/streamlink-312/lib/python3.12/site-packages/urllib3/connectionpool.py:1099
[cli][info] Available streams: 450p (worst), 1400k (best)
[cli][info] Opening stream: 1400k (hls)
...
```
Same result from my Windows 10 VM.

Streamlink dev venv
```
$ streamlink --no-config -l debug --http-no-ssl-verify "https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8"
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.12.0rc1
[cli][debug] OpenSSL:    OpenSSL 3.0.9 30 May 2023
[cli][debug] Streamlink: 6.10.0+39.gffa7db56
[cli][debug] Dependencies:
[cli][debug]  certifi: 2024.7.4
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 5.3.0
[cli][debug]  pycountry: 24.6.1
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.32.3
[cli][debug]  trio: 0.26.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.12.2
[cli][debug]  urllib3: 2.2.2
[cli][debug]  websocket-client: 1.8.0
[cli][debug] Arguments:
[cli][debug]  url=https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[cli][debug]  --no-config=True
[cli][debug]  --loglevel=debug
[cli][debug]  --http-no-ssl-verify=False
[cli][info] Found matching plugin hls for URL https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[plugins.hls][debug] URL=https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8; params={}
[utils.l10n][debug] Language code: en_US
[warnings][insecurerequestwarning] Unverified HTTPS request is being made to host 'stream.mediawork.cz'. Adding certificate verification is stro
ngly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  C:\Users\basti\Desktop\venv\streamlink-312\Lib\site-packages\urllib3\connectionpool.py:1099
Available streams: 450p (worst), 1400k (best)
```

Streamlink stable (portable build)
```
$ ./streamlink --no-config -l debug --http-no-ssl-verify "https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8"
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.12.5
[cli][debug] OpenSSL:    OpenSSL 3.0.13 30 Jan 2024
[cli][debug] Streamlink: 6.10.0
[cli][debug] Dependencies:
[cli][debug]  certifi: 2024.8.30
[cli][debug]  isodate: 0.6.1
[cli][debug]  lxml: 5.3.0
[cli][debug]  pycountry: 24.6.1
[cli][debug]  pycryptodome: 3.20.0
[cli][debug]  PySocks: 1.7.1
[cli][debug]  requests: 2.32.3
[cli][debug]  trio: 0.26.2
[cli][debug]  trio-websocket: 0.11.1
[cli][debug]  typing-extensions: 4.12.2
[cli][debug]  urllib3: 2.2.2
[cli][debug]  websocket-client: 1.8.0
[cli][debug] Arguments:
[cli][debug]  url=https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[cli][debug]  --no-config=True
[cli][debug]  --loglevel=debug
[cli][debug]  --http-no-ssl-verify=False
[session][debug] Loading plugin: hls
[cli][info] Found matching plugin hls for URL https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[plugins.hls][debug] URL=https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8; params={}
[utils.l10n][debug] Language code: en_US
[warnings][insecurerequestwarning] Unverified HTTPS request is being made to host 'stream.mediawork.cz'. Adding certificate verification is stro
ngly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  C:\Users\basti\Desktop\streamlink-6.10.0-1-py312-x86_64\pkgs\urllib3\connectionpool.py:1099
Available streams: 450p (worst), 1400k (best)
```
OK I can see that when i add --no-config, it works. 

```
streamlink --no-config --http-no-ssl-verify "https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8"
[cli][info] Found matching plugin hls for URL https://stream.mediawork.cz/retrotv/smil:retrotv2.smil/playlist.m3u8
[warnings][insecurerequestwarning] Unverified HTTPS request is being made to host 'stream.mediawork.cz'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  C:\Users\Zoran\AppData\Roaming\Python\Python312\site-packages\urllib3\connectionpool.py:1103
Available streams: 450p (worst), 1400k (best)

```

Without --no-config, it doesn't. 
I installed latest version from pip and latest pyopenssl
`--no-config` simply prevents all config files from being loaded. Your config file includes `--http-disable-dh`.

`--http-disable-dh` mounts a custom `HTTPAdapter` with a custom `SSLContext` that disables the diffie-hellman key exchange ([1](https://github.com/streamlink/streamlink/blob/6.10.0/src/streamlink/session/options.py#L414-L421), [2](https://github.com/streamlink/streamlink/blob/6.10.0/src/streamlink/session/http.py#L226-L235)). That custom `SSLContext` has default values except the disabled DH key exchange option (and disabled weak hashing algorithms).

When `--http-no-ssl-verify` is set, `requests` will set `conn.cert_reqs = "CERT_NONE"` ([1](https://github.com/psf/requests/blob/v2.32.3/src/requests/adapters.py#L337-L340)) on the connection pool, which then gets propagated down the chain until `urllib3` attempts to create and validate the TLS connection. The `cert_reqs` value basically ignores our custom `SSLContext`:
https://github.com/urllib3/urllib3/blob/2.2.3/src/urllib3/connection.py#L860-L871

This then causes a conflict when `urllib3` sets the `SSLContext.verify_mode` attribute to `ssl.CERT_NONE` (`cert_reqs == "CERT_NONE"`), since our `SSLContext.check_hostname` value is `True`, and it lead to the following error:
"Cannot set verify_mode to CERT_NONE when check_hostname is enabled").

----

I'll have to think about how to fix this in an elegant way. The only option I see right now is passing the `verify` option to the custom `HTTPAdapter` constructor, but that won't work if the options change while the HTTP session is alive.

For now, simply don't mix the two CLI arguments, because they are incompatible.



Patch:
diff --git a/src/streamlink/session/http.py b/src/streamlink/session/http.py
index 52b936f3457..1b120ac6517 100644
--- a/src/streamlink/session/http.py
+++ b/src/streamlink/session/http.py
@@ -222,6 +222,16 @@ def proxy_manager_for(self, *args, **kwargs):
         kwargs["ssl_context"] = self.poolmanager.connection_pool_kw["ssl_context"]
         return super().proxy_manager_for(*args, **kwargs)
 
+    def send(self, *args, verify=True, **kwargs):
+        # Always update the `check_hostname` and `verify_mode` attributes of our custom `SSLContext` before sending a request:
+        # If `verify` is `False`, then `requests` will set `cert_reqs=ssl.CERT_NONE` on the `HTTPSConnectionPool` object,
+        # which leads to `SSLContext` incompatibilities later on in `urllib3.connection._ssl_wrap_socket_and_match_hostname()`
+        # due to the default values of our `SSLContext`, namely `check_hostname=True` and `verify_mode=ssl.CERT_REQUIRED`.
+        if ssl_context := self.poolmanager.connection_pool_kw.get("ssl_context"):  # pragma: no branch
+            ssl_context.check_hostname = bool(verify)
+            ssl_context.verify_mode = ssl.CERT_NONE if not verify else ssl.CERT_REQUIRED
+        return super().send(*args, verify=verify, **kwargs)
+
 
 class TLSNoDHAdapter(SSLContextAdapter):
     def get_ssl_context(self) -> ssl.SSLContext:


Test patch:
diff --git a/tests/session/test_http.py b/tests/session/test_http.py
index 8a6b5c97475..c66de9a30ee 100644
--- a/tests/session/test_http.py
+++ b/tests/session/test_http.py
@@ -1,10 +1,14 @@
+import ssl
 from ssl import SSLContext
 from typing import Optional
 from unittest.mock import Mock, PropertyMock, call
 
 import pytest
 import requests
+from requests.adapters import HTTPAdapter
+from urllib3.response import HTTPResponse
 
+from streamlink import Streamlink
 from streamlink.exceptions import PluginError, StreamlinkDeprecationWarning
 from streamlink.session.http import HTTPSession, SSLContextAdapter, TLSNoDHAdapter, TLSSecLevel1Adapter
 from streamlink.session.http_useragents import FIREFOX
@@ -132,3 +136,52 @@ def test_proxymanager_ssl_context(self, proxy: str):
         ssl_context_poolmanager = adapter.poolmanager.connection_pool_kw.get("ssl_context")
         ssl_context_proxymanager = proxymanager.connection_pool_kw.get("ssl_context")
         assert ssl_context_poolmanager is ssl_context_proxymanager
+
+
+class TestHTTPSessionVerifyAndCustomSSLContext:
+    @pytest.fixture()
+    def adapter(self, session: Streamlink):
+        # The http-disable-dh session option mounts the TLSNoDHAdapter with a custom SSLContext
+        session.set_option("http-disable-dh", True)
+
+        adapter = session.http.adapters.get("https://")
+        assert isinstance(adapter, TLSNoDHAdapter)
+
+        return adapter
+
+    @pytest.fixture(autouse=True)
+    def _fake_request(self, monkeypatch: pytest.MonkeyPatch, session: Streamlink, adapter: HTTPAdapter):
+        class FakeHTTPResponse(HTTPResponse):
+            def stream(self, *_, **__):
+                yield b"mocked"
+
+        # Can't use requests_mock here, as it monkeypatches the adapter's send() method, which we want to test
+        req = requests.PreparedRequest()
+        resp = FakeHTTPResponse(status=200)
+        # noinspection PyTypeChecker
+        response = adapter.build_response(req, resp)
+        monkeypatch.setattr("requests.adapters.HTTPAdapter.send", Mock(return_value=response))
+
+        assert session.http.get("https://mocked/").text == "mocked"
+
+    @pytest.mark.parametrize(
+        ("session", "check_hostname", "verify_mode"),
+        [
+            pytest.param({"http-ssl-verify": True}, True, ssl.CERT_REQUIRED, id="verify"),
+            pytest.param({"http-ssl-verify": False}, False, ssl.CERT_NONE, id="no-verify"),
+        ],
+        indirect=["session"],
+    )
+    def test_ssl_context_attributes(
+        self,
+        session: Streamlink,
+        adapter: HTTPAdapter,
+        check_hostname: bool,
+        verify_mode: ssl.VerifyMode,
+    ):
+        assert session.http.verify is session.get_option("http-ssl-verify")
+
+        ssl_context = adapter.poolmanager.connection_pool_kw.get("ssl_context")
+        assert isinstance(ssl_context, SSLContext)
+        assert ssl_context.check_hostname is check_hostname
+        assert ssl_context.verify_mode is verify_mode