plugin.api.http_session: enforce_content_length

This commit is contained in:
bastimeyer 2021-06-02 13:47:21 +02:00 committed by back-to
parent 5bb8b9e11b
commit f22e3aac1d
1 changed files with 32 additions and 4 deletions

View File

@ -1,5 +1,7 @@
import time
import requests.adapters
import urllib3
from requests import Session
from streamlink.exceptions import PluginError
@ -7,16 +9,39 @@ from streamlink.packages.requests_file import FileAdapter
from streamlink.plugin.api import useragents
from streamlink.utils import parse_json, parse_xml
try:
from requests.packages import urllib3
try:
# We tell urllib3 to disable warnings about unverified HTTPS requests,
# because in some plugins we have to do unverified requests intentionally.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except (ImportError, AttributeError):
except AttributeError:
pass
__all__ = ["HTTPSession"]
class _HTTPResponse(urllib3.response.HTTPResponse):
def __init__(self, *args, **kwargs):
# Always enforce content length validation!
# This fixes a bug in requests which doesn't raise errors on HTTP responses where
# the "Content-Length" header doesn't match the response's body length.
# https://github.com/psf/requests/issues/4956#issuecomment-573325001
#
# Summary:
# This bug is related to urllib3.response.HTTPResponse.stream() which calls urllib3.response.HTTPResponse.read() as
# a wrapper for http.client.HTTPResponse.read(amt=...), where no http.client.IncompleteRead exception gets raised
# due to "backwards compatiblity" of an old bug if a specific amount is attempted to be read on an incomplete response.
#
# urllib3.response.HTTPResponse.read() however has an additional check implemented via the enforce_content_length
# parameter, but it doesn't check by default and requests doesn't set the parameter for enabling it either.
#
# Fix this by overriding urllib3.response.HTTPResponse's constructor and always setting enforce_content_length to True,
# as there is no way to make requests set this parameter on its own.
kwargs.update({"enforce_content_length": True})
super().__init__(*args, **kwargs)
# override all urllib3.response.HTTPResponse references in requests.adapters.HTTPAdapter.send
urllib3.connectionpool.HTTPConnectionPool.ResponseCls = _HTTPResponse
requests.adapters.HTTPResponse = _HTTPResponse
def _parse_keyvalue_list(val):
@ -151,3 +176,6 @@ class HTTPSession(Session):
res = schema.validate(res.text, name="response text", exception=PluginError)
return res
__all__ = ["HTTPSession"]