import argparse import logging from http.server import BaseHTTPRequestHandler, HTTPServer from coapthon.client.helperclient import HelperClient from coapthon.utils import parse_uri from coapthon.defines import Codes, DEFAULT_HC_PATH, HC_PROXY_DEFAULT_PORT, COAP_DEFAULT_PORT, LOCALHOST, BAD_REQUEST, \ NOT_IMPLEMENTED, CoAP_HTTP from coapthon.defines import COAP_PREFACE from urllib.parse import urlparse __author__ = "Marco Ieni, Davide Foti" __email__ = "marcoieni94@gmail.com, davidefoti.uni@gmail.com" logger = logging.getLogger(__name__) hc_path = DEFAULT_HC_PATH """ the class that realizes the HTTP-CoAP Proxy """ class HCProxy: """ This program implements an HTTP-CoAP Proxy without using external libraries. It is assumed that URI is formatted like this: http://hc_proxy_ip:proxy_port/hc/coap://server_coap_ip:server_coap_port/resource You can run this program passing the parameters from the command line or you can use the HCProxy class in your own project. """ def __init__(self, path=DEFAULT_HC_PATH, hc_port=HC_PROXY_DEFAULT_PORT, ip=LOCALHOST, coap_port=COAP_DEFAULT_PORT): """ Initialize the HC proxy. :param path: the path of the hc_proxy server :param hc_port: the port of the hc_proxy server :param ip: the ip of the hc_proxy server :param coap_port: the coap server port you want to reach """ global hc_path hc_path = HCProxy.get_formatted_path(path) self.hc_port = hc_port self.ip = ip self.coap_port = coap_port def run(self): """ Start the proxy. """ server_address = (self.ip, self.hc_port) hc_proxy = HTTPServer(server_address, HCProxyHandler) logger.info('Starting HTTP-CoAP Proxy...') hc_proxy.serve_forever() # the server listen to http://ip:hc_port/path @staticmethod def get_formatted_path(path): """ Uniform the path string :param path: the path :return: the uniform path """ if path[0] != '/': path = '/' + path if path[-1] != '/': path = '{0}/'.format(path) return path class CoapUri: # this class takes the URI from the HTTP URI """ Class that can manage and inbox the CoAP URI """ def __init__(self, coap_uri): self.uri = coap_uri self.host, self.port, self.path = parse_uri(coap_uri) def get_uri_as_list(self): """ Split the uri into :///;?# :return: the split uri """ return urlparse(self.uri) def get_payload(self): """ Return the query string of the uri. :return: the query string as a list """ temp = self.get_uri_as_list() query_string = temp[4] if query_string == "": return None # Bad request error code query_string_as_list = str.split(query_string, "=") return query_string_as_list[1] def __str__(self): return self.uri class HCProxyHandler(BaseHTTPRequestHandler): """ It maps the requests from HTTP to CoAP """ coap_uri = None client = None def set_coap_uri(self): """ Create a CoAP Uri """ self.coap_uri = CoapUri(self.path[len(hc_path):]) def do_initial_operations(self): """ Setup the client for interact with remote server """ if not self.request_hc_path_corresponds(): # the http URI of the request is not the same of the one specified by the admin for the hc proxy, # so I do not answer # For example the admin setup the http proxy URI like: "http://127.0.0.1:8080:/my_hc_path/" and the URI of # the requests asks for "http://127.0.0.1:8080:/another_hc_path/" return self.set_coap_uri() self.client = HelperClient(server=(self.coap_uri.host, self.coap_uri.port)) def do_GET(self): """ Perform a GET request """ self.do_initial_operations() coap_response = self.client.get(self.coap_uri.path) self.client.stop() logger.info("Server response: %s", coap_response.pretty_print()) self.set_http_response(coap_response) def do_HEAD(self): """ Perform a HEAD request """ self.do_initial_operations() # the HEAD method is not present in CoAP, so we treat it # like if it was a GET and then we exclude the body from the response # with send_body=False we say that we do not need the body, because it is a HEAD request coap_response = self.client.get(self.coap_uri.path) self.client.stop() logger.info("Server response: %s", coap_response.pretty_print()) self.set_http_header(coap_response) def do_POST(self): """ Perform a POST request """ # Doesn't do anything with posted data # print "uri: ", self.client_address, self.path self.do_initial_operations() payload = self.coap_uri.get_payload() if payload is None: logger.error("BAD POST REQUEST") self.send_error(BAD_REQUEST) return coap_response = self.client.post(self.coap_uri.path, payload) self.client.stop() logger.info("Server response: %s", coap_response.pretty_print()) self.set_http_response(coap_response) def do_PUT(self): """ Perform a PUT request """ self.do_initial_operations() payload = self.coap_uri.get_payload() if payload is None: logger.error("BAD PUT REQUEST") self.send_error(BAD_REQUEST) return logger.debug(payload) coap_response = self.client.put(self.coap_uri.path, payload) self.client.stop() logger.info("Server response: %s", coap_response.pretty_print()) self.set_http_response(coap_response) def do_DELETE(self): """ Perform a DELETE request """ self.do_initial_operations() coap_response = self.client.delete(self.coap_uri.path) self.client.stop() logger.info("Server response: %s", coap_response.pretty_print()) self.set_http_response(coap_response) def do_CONNECT(self): """ Perform a CONNECT request. Reply with error, not implemented in CoAP """ self.send_error(NOT_IMPLEMENTED) def do_OPTIONS(self): """ Perform a OPTIONS request. Reply with error, not implemented in CoAP """ self.send_error(NOT_IMPLEMENTED) def do_TRACE(self): """ Perform a TRACE request. Reply with error, not implemented in CoAP """ self.send_error(NOT_IMPLEMENTED) def request_hc_path_corresponds(self): """ Tells if the hc path of the request corresponds to that specified by the admin :return: a boolean that says if it corresponds or not """ uri_path = self.path.split(COAP_PREFACE) request_hc_path = uri_path[0] logger.debug("HCPATH: %s", hc_path) # print HC_PATH logger.debug("URI: %s", request_hc_path) if hc_path != request_hc_path: return False else: return True def set_http_header(self, coap_response): """ Sets http headers. :param coap_response: the coap response """ logger.debug( ("Server: %s\n"\ "codice risposta: %s\n"\ "PROXED: %s\n"\ "payload risposta: %s"), coap_response.source, coap_response.code, CoAP_HTTP[Codes.LIST[coap_response.code].name], coap_response.payload) self.send_response(int(CoAP_HTTP[Codes.LIST[coap_response.code].name])) self.send_header('Content-type', 'text/html') self.end_headers() def set_http_body(self, coap_response): """ Set http body. :param coap_response: the coap response """ if coap_response.payload is not None: body = "

", coap_response.payload, "

" self.wfile.write("".join(body)) else: self.wfile.write("

None

") def set_http_response(self, coap_response): """ Set http response. :param coap_response: the coap response """ self.set_http_header(coap_response) self.set_http_body(coap_response) return def get_command_line_args(): parser = argparse.ArgumentParser(description='Run the HTTP-CoAP Proxy.') parser.add_argument('-p', dest='path', default=DEFAULT_HC_PATH, help='the path of the hc_proxy server') parser.add_argument('-hp', dest='hc_port', default=HC_PROXY_DEFAULT_PORT, help='the port of the hc_proxy server') parser.add_argument('-ip', dest='ip', default=LOCALHOST, help='the ip of the hc_proxy server') parser.add_argument('-cp', dest='coap_port', default=COAP_DEFAULT_PORT, help='the coap server port you want to reach') return parser.parse_args() if __name__ == "__main__": args = get_command_line_args() hc_proxy = HCProxy(args.path, int(args.hc_port), args.ip, args.coap_port) hc_proxy.run()