283 lines
9.4 KiB
Python
283 lines
9.4 KiB
Python
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 <scheme>://<netloc>/<path>;<params>?<query>#<fragment>
|
|
|
|
: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 = "<html><body><h1>", coap_response.payload, "</h1></body></html>"
|
|
self.wfile.write("".join(body))
|
|
else:
|
|
self.wfile.write("<html><body><h1>None</h1></body></html>")
|
|
|
|
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()
|