Last active
December 1, 2019 17:29
-
-
Save SVilgelm/6192669 to your computer and use it in GitHub Desktop.
simple web server written in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import asyncio | |
from aiohttp import web | |
import argparse | |
import logging | |
from urllib import parse | |
logging.basicConfig(level=logging.DEBUG) | |
logger = logging.getLogger() | |
class ActionNotFoundError(Exception): | |
def __init__(self, action): | |
msg = 'Action "{}" not found'.format(action) | |
super().__init__(msg) | |
_rpc_actions = {} | |
async def _raise_not_found(request): | |
raise ActionNotFoundError(request.action) | |
async def _rpc_handler(request): | |
action=request.path.strip('/').lower() | |
request.action = action | |
func = _rpc_actions.get(action, _raise_not_found) | |
try: | |
response = await func(request) | |
code = 200 | |
except ActionNotFoundError as e: | |
response = str(e) | |
code = 404 | |
except Exception as e: | |
response = str(e) | |
code = 500 | |
return web.Response(text=response, status=code) | |
def rpc(func=None, name=None): | |
if func is None: | |
return lambda func: rpc(func=func, name=name) | |
if name is None: | |
name = func.__name__ | |
_rpc_actions[name.lower()] = func | |
return func | |
async def run_server(loop, host, port): | |
server = web.Server(_rpc_handler) | |
await loop.create_server(server, host, port) | |
logger.info('api server started at host %s and port %s', host, port) | |
logger.info('rpc actions: %s', ', '.join(_rpc_actions)) | |
# pause here for very long time by serving HTTP requests and | |
# waiting for keyboard interruption | |
await asyncio.sleep(100*3600) | |
def main(): | |
parser = argparse.ArgumentParser(conflict_handler='resolve') | |
parser.add_argument('-h', '--host', default='127.0.0.1') | |
parser.add_argument('-p', '--port', default=8125, type=int) | |
args = parser.parse_args() | |
loop = asyncio.get_event_loop() | |
try: | |
loop.run_until_complete(run_server(loop, args.host, args.port)) | |
except KeyboardInterrupt: | |
pass | |
loop.close() | |
############################################################################### | |
@rpc | |
async def ping(request): | |
return 'pong' | |
@rpc(name='test') | |
async def rpc_test_function(request): | |
return 'Ok' | |
############################################################################### | |
if __name__ == '__main__': | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import logging | |
from urllib import parse | |
from http import server | |
import socketserver | |
logging.basicConfig(level=logging.DEBUG) | |
logger = logging.getLogger() | |
class ActionNotFoundError(Exception): | |
def __init__(self, action): | |
msg = 'Action "{}" not found'.format(action) | |
super().__init__(msg) | |
class Request(object): | |
def __init__(self, action, headers, command, query, rfile=None): | |
self.action = action | |
self.headers = headers | |
self.command = command | |
self.query = query | |
self._rfile = rfile | |
self._content = None | |
@property | |
def content(self): | |
if self._content is None: | |
length = int(self.headers.get('content-length', 0)) | |
if length: | |
self._content = self._rfile.read(length).decode() | |
self._rfile.close() | |
else: | |
self._content = '' | |
return self._content | |
class ApiHandler(server.BaseHTTPRequestHandler): | |
@property | |
def _request(self): | |
u = parse.urlparse(self.path) | |
action = u.path.strip('/').lower() | |
if u.query: | |
query = parse.parse_qs(u.query, strict_parsing=True) | |
else: | |
query = None | |
return Request(action, self.headers, self.command, query, self.rfile) | |
def _handler(self): | |
req = self._request | |
try: | |
response = _rpc_handler(req) | |
self.send_response(200) | |
except ActionNotFoundError as e: | |
self.send_response(404) | |
response = str(e) | |
except Exception as e: | |
self.send_response(500) | |
response = str(e) | |
logger.debug('Request %s:%s; Content: %s; Response: %s', | |
req.command, req.action, req.content, response) | |
self.send_header("Content-type", 'text/plain') | |
self.end_headers() | |
self.wfile.write(bytes(response, 'utf-8')) | |
do_GET = do_POST = _handler | |
_rpc_actions = {} | |
def _raise_not_found(request): | |
raise ActionNotFoundError(request.action) | |
def _rpc_handler(request): | |
func = _rpc_actions.get(request.action, _raise_not_found) | |
return func(request) | |
def rpc(func=None, name=None): | |
if func is None: | |
return lambda func: rpc(func=func, name=name) | |
if name is None: | |
name = func.__name__ | |
_rpc_actions[name.lower()] = func | |
return func | |
def main(): | |
parser = argparse.ArgumentParser(conflict_handler='resolve') | |
parser.add_argument('-h', '--host', default='127.0.0.1') | |
parser.add_argument('-p', '--port', default=8125, type=int) | |
args = parser.parse_args() | |
host = args.host | |
port = args.port | |
srv = socketserver.ThreadingTCPServer((host, port), ApiHandler) | |
srv.allow_reuse_address = True | |
logger.info('api server started at host %s and port %s', host, port) | |
logger.info('rpc actions: %s', ', '.join(_rpc_actions)) | |
try: | |
srv.serve_forever() | |
except KeyboardInterrupt: | |
srv.server_close() | |
logger.info('Keyboard interrupt.') | |
except Exception: | |
logger.exception('Failed') | |
############################################################################### | |
@rpc | |
def ping(request): | |
return 'pong' | |
@rpc(name='test') | |
def rpc_test_function(request): | |
return 'Ok' | |
############################################################################### | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment