mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
Merge pull request #869 from JackDandy/feature/UpdateCacheCtrl
Update cachecontrol library 0.11.5 to 0.11.7 (3b3b776).
This commit is contained in:
commit
ab5682a2c8
11 changed files with 91 additions and 53 deletions
|
@ -10,6 +10,7 @@
|
||||||
* Change webserver startup to correctly use xheaders in reverse proxy or load balance set-ups
|
* Change webserver startup to correctly use xheaders in reverse proxy or load balance set-ups
|
||||||
* Update backports_abc 0.4 to 0.5
|
* Update backports_abc 0.4 to 0.5
|
||||||
* Update Beautiful Soup 4.4.0 (r397) to 4.5.3 (r439)
|
* Update Beautiful Soup 4.4.0 (r397) to 4.5.3 (r439)
|
||||||
|
* Update cachecontrol library 0.11.5 to 0.11.7 (3b3b776)
|
||||||
|
|
||||||
|
|
||||||
[develop changelog]
|
[develop changelog]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
Libs with customisations...
|
Libs with customisations...
|
||||||
|
|
||||||
/lib/cachecontrol/caches/file_cache.py
|
|
||||||
/lib/dateutil/zoneinfo/__init__.py
|
/lib/dateutil/zoneinfo/__init__.py
|
||||||
/lib/hachoir_core/config.py
|
/lib/hachoir_core/config.py
|
||||||
/lib/hachoir_core/stream/input_helpers.py
|
/lib/hachoir_core/stream/input_helpers.py
|
||||||
|
|
|
@ -4,7 +4,7 @@ Make it easy to import from cachecontrol without long namespaces.
|
||||||
"""
|
"""
|
||||||
__author__ = 'Eric Larson'
|
__author__ = 'Eric Larson'
|
||||||
__email__ = 'eric@ionrock.org'
|
__email__ = 'eric@ionrock.org'
|
||||||
__version__ = '0.11.5'
|
__version__ = '0.11.7'
|
||||||
|
|
||||||
from .wrapper import CacheControl
|
from .wrapper import CacheControl
|
||||||
from .adapter import CacheControlAdapter
|
from .adapter import CacheControlAdapter
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import types
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
|
@ -55,6 +56,10 @@ class CacheControlAdapter(HTTPAdapter):
|
||||||
cached response
|
cached response
|
||||||
"""
|
"""
|
||||||
if not from_cache and request.method == 'GET':
|
if not from_cache and request.method == 'GET':
|
||||||
|
# Check for any heuristics that might update headers
|
||||||
|
# before trying to cache.
|
||||||
|
if self.heuristic:
|
||||||
|
response = self.heuristic.apply(response)
|
||||||
|
|
||||||
# apply any expiration heuristics
|
# apply any expiration heuristics
|
||||||
if response.status == 304:
|
if response.status == 304:
|
||||||
|
@ -82,11 +87,6 @@ class CacheControlAdapter(HTTPAdapter):
|
||||||
elif response.status == 301:
|
elif response.status == 301:
|
||||||
self.controller.cache_response(request, response)
|
self.controller.cache_response(request, response)
|
||||||
else:
|
else:
|
||||||
# Check for any heuristics that might update headers
|
|
||||||
# before trying to cache.
|
|
||||||
if self.heuristic:
|
|
||||||
response = self.heuristic.apply(response)
|
|
||||||
|
|
||||||
# Wrap the response file with a wrapper that will cache the
|
# Wrap the response file with a wrapper that will cache the
|
||||||
# response when the stream has been consumed.
|
# response when the stream has been consumed.
|
||||||
response._fp = CallbackFileWrapper(
|
response._fp = CallbackFileWrapper(
|
||||||
|
@ -97,6 +97,14 @@ class CacheControlAdapter(HTTPAdapter):
|
||||||
response,
|
response,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if response.chunked:
|
||||||
|
super_update_chunk_length = response._update_chunk_length
|
||||||
|
|
||||||
|
def _update_chunk_length(self):
|
||||||
|
super_update_chunk_length()
|
||||||
|
if self.chunk_left == 0:
|
||||||
|
self._fp._close()
|
||||||
|
response._update_chunk_length = types.MethodType(_update_chunk_length, response)
|
||||||
|
|
||||||
resp = super(CacheControlAdapter, self).build_response(
|
resp = super(CacheControlAdapter, self).build_response(
|
||||||
request, response
|
request, response
|
||||||
|
|
|
@ -1,18 +1,2 @@
|
||||||
from textwrap import dedent
|
from .file_cache import FileCache # noqa
|
||||||
|
from .redis_cache import RedisCache # noqa
|
||||||
try:
|
|
||||||
from .file_cache import FileCache
|
|
||||||
except ImportError:
|
|
||||||
notice = dedent('''
|
|
||||||
NOTE: In order to use the FileCache you must have
|
|
||||||
lockfile installed. You can install it via pip:
|
|
||||||
pip install lockfile
|
|
||||||
''')
|
|
||||||
print(notice)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
import redis
|
|
||||||
from .redis_cache import RedisCache
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
from textwrap import dedent
|
||||||
from lockfile import LockFile
|
|
||||||
from lockfile.mkdirlockfile import MkdirLockFile
|
|
||||||
|
|
||||||
from ..cache import BaseCache
|
from ..cache import BaseCache
|
||||||
from ..controller import CacheController
|
from ..controller import CacheController
|
||||||
|
@ -55,11 +53,22 @@ class FileCache(BaseCache):
|
||||||
if use_dir_lock is not None and lock_class is not None:
|
if use_dir_lock is not None and lock_class is not None:
|
||||||
raise ValueError("Cannot use use_dir_lock and lock_class together")
|
raise ValueError("Cannot use use_dir_lock and lock_class together")
|
||||||
|
|
||||||
if use_dir_lock:
|
try:
|
||||||
lock_class = MkdirLockFile
|
from lockfile import LockFile
|
||||||
|
from lockfile.mkdirlockfile import MkdirLockFile
|
||||||
|
except ImportError:
|
||||||
|
notice = dedent("""
|
||||||
|
NOTE: In order to use the FileCache you must have
|
||||||
|
lockfile installed. You can install it via pip:
|
||||||
|
pip install lockfile
|
||||||
|
""")
|
||||||
|
raise ImportError(notice)
|
||||||
|
else:
|
||||||
|
if use_dir_lock:
|
||||||
|
lock_class = MkdirLockFile
|
||||||
|
|
||||||
if lock_class is None:
|
elif lock_class is None:
|
||||||
lock_class = LockFile
|
lock_class = LockFile
|
||||||
|
|
||||||
self.directory = directory
|
self.directory = directory
|
||||||
self.forever = forever
|
self.forever = forever
|
||||||
|
@ -67,7 +76,6 @@ class FileCache(BaseCache):
|
||||||
self.dirmode = dirmode
|
self.dirmode = dirmode
|
||||||
self.lock_class = lock_class
|
self.lock_class = lock_class
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode(x):
|
def encode(x):
|
||||||
return hashlib.sha224(x.encode()).hexdigest()
|
return hashlib.sha224(x.encode()).hexdigest()
|
||||||
|
@ -103,8 +111,11 @@ class FileCache(BaseCache):
|
||||||
|
|
||||||
def delete(self, key):
|
def delete(self, key):
|
||||||
name = self._fn(key)
|
name = self._fn(key)
|
||||||
if not self.forever and os.path.exists(name):
|
if not self.forever:
|
||||||
os.remove(name)
|
try:
|
||||||
|
os.remove(name)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def url_to_file_path(url, filecache):
|
def url_to_file_path(url, filecache):
|
||||||
|
|
|
@ -6,11 +6,11 @@ from datetime import datetime
|
||||||
def total_seconds(td):
|
def total_seconds(td):
|
||||||
"""Python 2.6 compatability"""
|
"""Python 2.6 compatability"""
|
||||||
if hasattr(td, 'total_seconds'):
|
if hasattr(td, 'total_seconds'):
|
||||||
return td.total_seconds()
|
return int(td.total_seconds())
|
||||||
|
|
||||||
ms = td.microseconds
|
ms = td.microseconds
|
||||||
secs = (td.seconds + td.days * 24 * 3600)
|
secs = (td.seconds + td.days * 24 * 3600)
|
||||||
return (ms + secs * 10**6) / 10**6
|
return int((ms + secs * 10**6) / 10**6)
|
||||||
|
|
||||||
|
|
||||||
class RedisCache(object):
|
class RedisCache(object):
|
||||||
|
|
|
@ -239,6 +239,16 @@ class CacheController(object):
|
||||||
|
|
||||||
response_headers = CaseInsensitiveDict(response.headers)
|
response_headers = CaseInsensitiveDict(response.headers)
|
||||||
|
|
||||||
|
# If we've been given a body, our response has a Content-Length, that
|
||||||
|
# Content-Length is valid then we can check to see if the body we've
|
||||||
|
# been given matches the expected size, and if it doesn't we'll just
|
||||||
|
# skip trying to cache it.
|
||||||
|
if (body is not None and
|
||||||
|
"content-length" in response_headers and
|
||||||
|
response_headers["content-length"].isdigit() and
|
||||||
|
int(response_headers["content-length"]) != len(body)):
|
||||||
|
return
|
||||||
|
|
||||||
cc_req = self.parse_cache_control(request.headers)
|
cc_req = self.parse_cache_control(request.headers)
|
||||||
cc = self.parse_cache_control(response_headers)
|
cc = self.parse_cache_control(response_headers)
|
||||||
|
|
||||||
|
@ -280,7 +290,7 @@ class CacheController(object):
|
||||||
elif 'date' in response_headers:
|
elif 'date' in response_headers:
|
||||||
# cache when there is a max-age > 0
|
# cache when there is a max-age > 0
|
||||||
if cc and cc.get('max-age'):
|
if cc and cc.get('max-age'):
|
||||||
if int(cc['max-age']) > 0:
|
if cc['max-age'].isdigit() and int(cc['max-age']) > 0:
|
||||||
logger.debug('Caching b/c date exists and max-age > 0')
|
logger.debug('Caching b/c date exists and max-age > 0')
|
||||||
self.cache.set(
|
self.cache.set(
|
||||||
cache_url,
|
cache_url,
|
||||||
|
|
|
@ -45,19 +45,34 @@ class CallbackFileWrapper(object):
|
||||||
# TODO: Add some logging here...
|
# TODO: Add some logging here...
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _close(self):
|
||||||
|
if self.__callback:
|
||||||
|
self.__callback(self.__buf.getvalue())
|
||||||
|
|
||||||
|
# We assign this to None here, because otherwise we can get into
|
||||||
|
# really tricky problems where the CPython interpreter dead locks
|
||||||
|
# because the callback is holding a reference to something which
|
||||||
|
# has a __del__ method. Setting this to None breaks the cycle
|
||||||
|
# and allows the garbage collector to do it's thing normally.
|
||||||
|
self.__callback = None
|
||||||
|
|
||||||
def read(self, amt=None):
|
def read(self, amt=None):
|
||||||
data = self.__fp.read(amt)
|
data = self.__fp.read(amt)
|
||||||
self.__buf.write(data)
|
self.__buf.write(data)
|
||||||
|
|
||||||
if self.__is_fp_closed():
|
if self.__is_fp_closed():
|
||||||
if self.__callback:
|
self._close()
|
||||||
self.__callback(self.__buf.getvalue())
|
|
||||||
|
return data
|
||||||
# We assign this to None here, because otherwise we can get into
|
|
||||||
# really tricky problems where the CPython interpreter dead locks
|
def _safe_read(self, amt):
|
||||||
# because the callback is holding a reference to something which
|
data = self.__fp._safe_read(amt)
|
||||||
# has a __del__ method. Setting this to None breaks the cycle
|
if amt == 2 and data == b'\r\n':
|
||||||
# and allows the garbage collector to do it's thing normally.
|
# urllib executes this read to toss the CRLF at the end
|
||||||
self.__callback = None
|
# of the chunk.
|
||||||
|
return data
|
||||||
|
|
||||||
|
self.__buf.write(data)
|
||||||
|
if self.__is_fp_closed():
|
||||||
|
self._close()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -40,10 +40,14 @@ class BaseHeuristic(object):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def apply(self, response):
|
def apply(self, response):
|
||||||
warning_header_value = self.warning(response)
|
updated_headers = self.update_headers(response)
|
||||||
response.headers.update(self.update_headers(response))
|
|
||||||
if warning_header_value is not None:
|
if updated_headers:
|
||||||
response.headers.update({'Warning': warning_header_value})
|
response.headers.update(updated_headers)
|
||||||
|
warning_header_value = self.warning(response)
|
||||||
|
if warning_header_value is not None:
|
||||||
|
response.headers.update({'Warning': warning_header_value})
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,12 @@ class Serializer(object):
|
||||||
|
|
||||||
body_raw = cached["response"].pop("body")
|
body_raw = cached["response"].pop("body")
|
||||||
|
|
||||||
|
headers = CaseInsensitiveDict(data=cached['response']['headers'])
|
||||||
|
if headers.get('transfer-encoding', '') == 'chunked':
|
||||||
|
headers.pop('transfer-encoding')
|
||||||
|
|
||||||
|
cached['response']['headers'] = headers
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body = io.BytesIO(body_raw)
|
body = io.BytesIO(body_raw)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -168,7 +174,7 @@ class Serializer(object):
|
||||||
def _loads_v2(self, request, data):
|
def _loads_v2(self, request, data):
|
||||||
try:
|
try:
|
||||||
cached = json.loads(zlib.decompress(data).decode("utf8"))
|
cached = json.loads(zlib.decompress(data).decode("utf8"))
|
||||||
except ValueError:
|
except (ValueError, zlib.error):
|
||||||
return
|
return
|
||||||
|
|
||||||
# We need to decode the items that we've base64 encoded
|
# We need to decode the items that we've base64 encoded
|
||||||
|
|
Loading…
Reference in a new issue