import copy import logging from coapthon.messages.request import Request from coapclient import HelperClient from coapthon.messages.response import Response from coapthon import defines from coapthon.resources.remoteResource import RemoteResource from coapthon.utils import parse_uri __author__ = 'Giacomo Tanganelli' logger = logging.getLogger(__name__) class ForwardLayer(object): """ Class used by Proxies to forward messages. """ def __init__(self, server): self._server = server def receive_request(self, transaction): """ Setup the transaction for forwarding purposes on Forward Proxies. :type transaction: Transaction :param transaction: the transaction that owns the request :rtype : Transaction :return: the edited transaction """ uri = transaction.request.proxy_uri if uri is None: transaction.response = Response() transaction.response.destination = transaction.request.source transaction.response.token = transaction.request.token transaction.response.type = defines.Types["RST"] transaction.response.code = defines.Codes.BAD_REQUEST.number return transaction host, port, path = parse_uri(uri) path = str("/" + path) transaction.response = Response() transaction.response.destination = transaction.request.source transaction.response.token = transaction.request.token return self._forward_request(transaction, (host, port), path) def receive_request_reverse(self, transaction): """ Setup the transaction for forwarding purposes on Reverse Proxies. :type transaction: Transaction :param transaction: the transaction that owns the request :rtype : Transaction :return: the edited transaction """ wkc_resource_is_defined = defines.DISCOVERY_URL in self._server.root path = str("/" + transaction.request.uri_path) transaction.response = Response() transaction.response.destination = transaction.request.source transaction.response.token = transaction.request.token if path == defines.DISCOVERY_URL and not wkc_resource_is_defined: transaction = self._server.resourceLayer.discover(transaction) else: new = False if transaction.request.code == defines.Codes.POST.number: new_paths = self._server.root.with_prefix(path) new_path = "/" for tmp in new_paths: if len(tmp) > len(new_path): new_path = tmp if path != new_path: new = True path = new_path try: resource = self._server.root[path] except KeyError: resource = None if resource is None or path == '/': # Not Found transaction.response.code = defines.Codes.NOT_FOUND.number else: transaction.resource = resource transaction = self._handle_request(transaction, new) return transaction @staticmethod def _forward_request(transaction, destination, path): """ Forward requests. :type transaction: Transaction :param transaction: the transaction that owns the request :param destination: the destination of the request (IP, port) :param path: the path of the request. :rtype : Transaction :return: the edited transaction """ client = HelperClient(destination) request = Request() request.options = copy.deepcopy(transaction.request.options) del request.block2 del request.block1 del request.uri_path del request.proxy_uri del request.proxy_schema # TODO handle observing del request.observe # request.observe = transaction.request.observe request.uri_path = path request.destination = destination request.payload = transaction.request.payload request.code = transaction.request.code response = client.send_request(request) client.stop() if response is not None: transaction.response.payload = response.payload transaction.response.code = response.code transaction.response.options = response.options else: transaction.response.code = defines.Codes.SERVICE_UNAVAILABLE.number return transaction def _handle_request(self, transaction, new_resource): """ Forward requests. Used by reverse proxies to also create new virtual resources on the proxy in case of created resources :type new_resource: bool :type transaction: Transaction :param transaction: the transaction that owns the request :rtype : Transaction :param new_resource: if the request will generate a new resource :return: the edited transaction """ client = HelperClient(transaction.resource.remote_server) request = Request() request.options = copy.deepcopy(transaction.request.options) del request.block2 del request.block1 del request.uri_path del request.proxy_uri del request.proxy_schema # TODO handle observing del request.observe # request.observe = transaction.request.observe request.uri_path = "/".join(transaction.request.uri_path.split("/")[1:]) request.destination = transaction.resource.remote_server request.payload = transaction.request.payload request.code = transaction.request.code logger.info("forward_request - " + str(request)) response = client.send_request(request) client.stop() logger.info("forward_response - " + str(response)) transaction.response.payload = response.payload transaction.response.code = response.code transaction.response.options = response.options if response.code == defines.Codes.CREATED.number: lp = transaction.response.location_path del transaction.response.location_path transaction.response.location_path = transaction.request.uri_path.split("/")[0] + "/" + lp # TODO handle observing if new_resource: resource = RemoteResource('server', transaction.resource.remote_server, lp, coap_server=self, visible=True, observable=False, allow_children=True) self._server.add_resource(transaction.response.location_path, resource) if response.code == defines.Codes.DELETED.number: del self._server.root["/" + transaction.request.uri_path] return transaction