petale/petale/utils.py

78 lines
2.8 KiB
Python

# Petale - Simple App as Key/Value Storage Interface
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import hashlib
import logging
from functools import wraps
DEFAULT_HASH_ALGO = 'sha1'
def logit(func):
@wraps(func)
def wrapper(self, request, *args, **kwargs):
logger = logging.getLogger('petale')
req_url = '%s %s %s' % (request.method, request.path, request.GET.urlencode())
logger.info(req_url, extra={'request_url': req_url})
req_headers = ''.join(
['%s: %s | ' % (k, v) for k, v in request.META.items() if k.isupper()])
logger.debug('Request Headers: %s', req_headers, extra={'request_headers': req_headers})
response = func(self, request, *args, **kwargs)
resp_headers = ''.join(['%s: %s | ' % (k, v) for k, v in response.items()])
logger.debug('Response Headers: %s', resp_headers,
extra={'response_headers': resp_headers})
if hasattr(response, 'data'):
logger.debug('Response Data: %r', response.data,
extra={'response_body': response.data})
logger.debug('Response Status Code: %s', response.status_code,
extra={'response_status_code': response.status_code})
return response
return wrapper
def etag(stream):
# f must be a Django file object
digest = hashlib.new(DEFAULT_HASH_ALGO)
if hasattr(stream, 'chunks'):
for chunk in stream.chunks():
digest.update(chunk)
elif hasattr(stream, 'isdecimal'):
digest.update(stream.encode('utf-8'))
else:
digest.update(stream)
return '"%s:%s"' % (DEFAULT_HASH_ALGO, digest.hexdigest())
class StreamingHash(object):
def __init__(self, readable, hash_algo=DEFAULT_HASH_ALGO):
self.readable = readable
self.hash_algo = hash_algo
self.digest = hashlib.new(hash_algo)
self.size = 0
def read(self, n=-1): # pylint: disable=C0103
buf = self.readable.read(n)
self.size += len(buf)
self.digest.update(buf)
return buf
def hexdigest(self):
return self.digest.hexdigest()
def etag(self):
return '"%s:%s"' % (self.hash_algo, self.hexdigest())