You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
6.9 KiB
Python

import time
import logging
from .coaplrucache import CoapLRUCache
from coapthon import utils
from coapthon.messages.request import *
__author__ = 'Emilio Vallati'
logger = logging.getLogger(__name__)
class Cache(object):
def __init__(self, mode, max_dim):
"""
:param max_dim: max number of elements in the cache
:param mode: used to differentiate between a cache used in a forward-proxy or in a reverse-proxy
"""
self.max_dimension = max_dim
self.mode = mode
self.cache = CoapLRUCache(max_dim)
def cache_add(self, request, response):
"""
checks for full cache and valid code before updating the cache
:param request:
:param response:
:return:
"""
logger.debug("adding response to the cache")
"""
checking for valid code
- """
code = response.code
try:
utils.check_code(code)
except utils.InvalidResponseCode: # pragma no cover
logger.error("Invalid response code")
return
"""
return if max_age is 0
"""
if response.max_age == 0:
return
"""
Initialising new cache element based on the mode and updating the cache
"""
if self.mode == defines.FORWARD_PROXY:
new_key = CacheKey(request)
else:
new_key = ReverseCacheKey(request)
logger.debug("MaxAge = {maxage}".format(maxage=response.max_age))
new_element = CacheElement(new_key, response, request, response.max_age)
self.cache.update(new_key, new_element)
logger.debug("Cache Size = {size}".format(size=self.cache.debug_print()))
def search_related(self, request):
logger.debug("Cache Search Request")
if self.cache.is_empty() is True:
logger.debug("Empty Cache")
return None
"""
extracting everything from the cache
"""
result = []
items = list(self.cache.cache.items())
for key, item in items:
element = self.cache.get(item.key)
logger.debug("Element : {elm}".format(elm=str(element)))
if request.proxy_uri == element.uri:
result.append(item)
return result
def search_response(self, request):
"""
creates a key from the request and searches the cache with it
:param request:
:return CacheElement: returns None if there's a cache miss
"""
logger.debug("Cache Search Response")
if self.cache.is_empty() is True:
logger.debug("Empty Cache")
return None
"""
create a new cache key from the request
"""
if self.mode == defines.FORWARD_PROXY:
search_key = CacheKey(request)
else:
search_key = ReverseCacheKey(request)
response = self.cache.get(search_key)
return response
def validate(self, request, response):
"""
refreshes a resource when a validation response is received
:param request:
:param response:
:return:
"""
element = self.search_response(request)
if element is not None:
element.cached_response.options = response.options
element.freshness = True
element.max_age = response.max_age
element.creation_time = time.time()
element.uri = request.proxy_uri
def mark(self, element):
"""
marks the requested resource in the cache as not fresh
:param element:
:return:
"""
logger.debug("Element : {elm}".format(elm=str(element)))
if element is not None:
logger.debug("Mark as not fresh")
element.freshness = False
logger.debug(str(self.cache))
"""
class for the element contained in the cache
"""
class CacheElement(object):
def __init__(self, cache_key, response, request, max_age=60):
"""
:param cache_key: the key used to search for the element in the cache
:param response: the server response to store
:param max_age: maximum number of seconds that the resource is considered fresh
"""
self.freshness = True
self.key = cache_key
self.cached_response = response
self.max_age = max_age
self.creation_time = time.time()
self.uri = request.proxy_uri
def __str__(self):
return "Key: {key}, Fresh: {fresh}, URI: {uri}, MaxAge: {maxage}, CreationTime: {ct}, Response: {resp}"\
.format(key=str(self.key), fresh=self.freshness, uri=self.uri, maxage=self.max_age, ct=self.creation_time,
resp=str(self.cached_response))
"""
class for the key used to search elements in the cache (forward-proxy only)
"""
class CacheKey(object):
def __init__(self, request):
"""
:param request:
"""
logger.debug("Creating Key")
self._payload = request.payload
self._method = request.code
"""
making a list of the options that do not have a nocachekey option number and are not uri-path, uri-host, uri-port, uri-query
"""
self._options = []
for option in request.options:
if (utils.check_nocachekey(option) is False) and (utils.is_uri_option(option.number) is False):
self._options.append(option)
"""
creating a usable key for the cache structure
"""
option_str = ', '.join(map(str, self._options))
self._list = [self._payload, self._method, option_str]
self.hashkey = ', '.join(map(str, self._list))
logger.debug(self)
def __str__(self):
msg = ""
for opt in self._options:
msg += "{name}: {value}, ".format(name=opt.name, value=opt.value)
return "Payload: {p}, Method: {m}, Options: [{o}]".format(p=self._payload, m=self._method, o=msg)
"""
class for the key used to search elements in the cache (reverse-proxy only)
"""
class ReverseCacheKey(object):
def __init__(self, request):
"""
:param request:
"""
self._payload = request.payload
self._method = request.code
"""
making a list of the options that do not have a nocachekey option number
"""
self._options = []
for option in request.options:
if utils.check_nocachekey(option) is False:
self._options.append(option)
"""
creating a usable key for the cache structure
"""
option_str = ', '.join(map(str, self._options))
self.hashkey = (self._payload, self._method, option_str)
def __str__(self):
msg = ""
for opt in self._options:
msg += "{name}: {value}, ".format(name=opt.name, value=opt.value)
return "Payload: {p}, Method: {m}, Options: [{o}]".format(p=self._payload, m=self._method, o=msg)