--- /dev/null
+#!/usr/bin/env python3
+# ==================================================================================
+# Copyright (c) 2022 Nokia
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==================================================================================
+import socket
+import http.server
+import threading
+
+
+def initResponse(status=200, response="OK"):
+
+ """
+ initResponse
+ init the reponse data for handler to get all details defined
+
+ Parameters
+ ----------
+ status: int
+ http status code
+ response: string
+ http response text
+ Returns
+ -------
+ response - http response text
+ status - http status code
+ payload - payload data for the client (response data text or attachment file data)
+ attachment - file name of the attached payload data
+ mode text (utf-8) or binary data
+ """
+ return {'response': response, 'status': status, 'payload': None, 'ctype': 'application/json', 'attachment': None, 'mode': 'plain'}
+
+
+class RestHandler(http.server.BaseHTTPRequestHandler):
+
+ def _findUrihandler(self, uri, keys):
+ for key in keys:
+ value = keys[key]
+ if uri.find(value['uri']) >= 0:
+ return key, value
+ return None, None
+
+ def _sendResponse(self, response):
+ # sends the reponse according to the initResponse() response data
+ self.send_response(response['status'])
+ self.send_header("Server-name", "XAPP REST SERVER 0.9")
+ self.send_header('Content-type', response['ctype'])
+
+ if response['payload'] is not None:
+ # payload has been set
+ length = len(response['payload'])
+ if length != 0:
+ self.send_header('Content-length', length)
+ if response['attachment'] is not None:
+ self.send_header('Content-Disposition', "attachment; filename=" + response['attachment'])
+ self.end_headers()
+ if response['payload'] is not None:
+ if response['mode'] == 'plain':
+ # ascii mode
+ self.wfile.write(response['payload'].encode('utf-8'))
+ elif response['mode'] == 'binary':
+ # binary mode
+ self.wfile.write(response['payload'])
+
+ def add_handler(self, method=None, name=None, uri=None, callback=None):
+ """
+ Adds the function handler for given uri. The function callback is matched in first matching
+ uri. So prepare your handlers setup in such a way that those won't override each other. For example you can setup
+ usual xapp handler in this list:
+
+ server = ricrest.ThreadedHTTPServer(address, port)
+ server.handler.add_handler(self.server.handler, "GET", "config", "/ric/v1/config", self.configGetHandler)
+ server.handler.add_handler(self.server.handler, "GET", "healthAlive", "/ric/v1/health/alive", self.healthyGetAliveHandler)
+ server.handler.add_handler(self.server.handler, "GET", "healthReady", "/ric/v1/health/ready", self.healthyGetReadyHandler)
+ server.handler.add_handler(self.server.handler, "GET", "symptomdata", "/ric/v1/symptomdata", self.symptomdataGetHandler)
+
+ Parameters
+ ----------
+ method string
+ http method GET, POST, DELETE
+ name string
+ unique name - used for map name
+ uri string
+ http uri part which triggers the callback function
+ cb function
+ function to be used for http method processing
+ """
+ if not hasattr(self, 'handlers'):
+ # init method can't be used becuase it has been inherited from base object
+ # so check the handlers existence and create if not defined
+ self.lock = threading.Lock()
+ self.handlers = dict()
+ self.handlers["get"] = dict()
+ self.handlers["post"] = dict()
+ self.handlers["delete"] = dict()
+ self.lock.acquire()
+ if method == "GET":
+ self.handlers["get"][name] = dict()
+ self.handlers["get"][name]['uri'] = uri
+ self.handlers["get"][name]['cb'] = callback
+ elif method == "POST":
+ self.handlers["post"][name] = dict()
+ self.handlers["post"][name]['uri'] = uri
+ self.handlers["post"][name]['cb'] = callback
+ elif method == "DELETE":
+ self.handlers["delete"][name] = dict()
+ self.handlers["delete"][name]['uri'] = uri
+ self.handlers["delete"][name]['cb'] = callback
+ self.lock.release()
+
+ def do_GET(self):
+ try:
+ response = initResponse(status=404, response='Not Found')
+ cbname, hndl = self._findUrihandler(self.path, self.handlers['get'])
+ if hndl is not None:
+ # call the defined callback handler
+ response = hndl['cb'](cbname, self.path, None, self.headers['Content-Type'])
+ self._sendResponse(response)
+
+ except (socket.error, IOError):
+ pass
+
+ def do_DELETE(self):
+ try:
+ response = initResponse(status=404, response='Not Found')
+ cbname, hndl = self._findUrihandler(self.path, self.handlers['delete'])
+ if hndl is not None:
+ # call the defined callback handler
+ response = hndl['cb'](cbname, self.path, None, self.headers['Content-Type'])
+ self._sendResponse(response)
+ except (socket.error, IOError):
+ pass
+
+ def do_POST(self):
+ try:
+ response = initResponse(status=404, response='Not Found')
+ cbname, hndl = self._findUrihandler(self.path, self.handlers['post'])
+ if hndl is not None:
+ data = self.rfile.read(int(self.headers['Content-Length']))
+ # call the defined callback handler
+ response = hndl['cb'](cbname, self.path, data, self.headers['Content-Type'])
+ print(response)
+ self._sendResponse(response)
+ except (socket.error, IOError):
+ pass
+
+
+class ThreadedHTTPServer(object):
+
+ handler = RestHandler
+ server_class = http.server.HTTPServer
+
+ def __init__(self, host, port):
+ """
+ init
+
+ Parameters
+ ----------
+ host string
+ http listen interface ip ("0.0.0.0" binds all interfaces)
+ port int
+ listen service port
+ """
+ self.server = self.server_class((host, port), self.handler)
+ self.server_thread = threading.Thread(target=self.server.serve_forever)
+ self.server_thread.daemon = True
+
+ def start(self):
+ """
+ start
+ starts the thread serving http requests
+ """
+ self.server_thread.start()
+
+ def stop(self):
+ """
+ stop
+ stops thread serving http requests
+ """
+ self.server.socket.close()
+ self.server.server_close()
+ self.server.shutdown()