Skip to content

Instantly share code, notes, and snippets.

@SVilgelm
Last active December 1, 2019 17:29
Show Gist options
  • Save SVilgelm/6192669 to your computer and use it in GitHub Desktop.
Save SVilgelm/6192669 to your computer and use it in GitHub Desktop.
simple web server written in python
#!/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()
#!/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