123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- #!/usr/bin/env python
- import os
- import sys
- import falcon
- from db_orm import session, World, Fortune
- from helpers import load_template, FortuneTuple, generate_ids, sanitize
- from operator import attrgetter
- from random import randint
- from email.utils import formatdate
- # ------------------------------------------------------------------
- # setup
- wsgi = app = falcon.App()
- response_server = "Falcon"
- response_add_date = False
- def add_ext_headers(response):
- if response_server:
- response.set_header('Server', response_server)
- if response_add_date:
- response.set_header('Date', formatdate(timeval=None, localtime=False, usegmt=True))
- if os.getenv('USE_ORJSON', "0") == "1":
- import orjson
- # custom JSON handler
- JSONHandler = falcon.media.JSONHandler(dumps=orjson.dumps, loads=orjson.loads)
- extra_handlers = {
- "application/json": JSONHandler,
- "application/json; charset=UTF-8": JSONHandler
- }
- wsgi.req_options.media_handlers.update(extra_handlers)
- wsgi.resp_options.media_handlers.update(extra_handlers)
- # ------------------------------------------------------------------
- # resource endpoints
- class JSONResource(object):
- def on_get(self, request, response):
- add_ext_headers(response)
- response.media = {'message': "Hello, world!"}
- class SingleQuery(object):
- @session(serializable=False)
- def on_get(self, request, response):
- wid = randint(1, 10000)
- world = World[wid]
- add_ext_headers(response)
- response.media = world.to_dict()
- class MultipleQueries(object):
- @session(serializable=False)
- def on_get(self, request, response, num):
- num = sanitize(num)
- worlds = [World[ident].to_dict() for ident in generate_ids(num)]
- add_ext_headers(response)
- response.media = worlds
- class UpdateQueries(object):
- @session(serializable=False)
- def on_get(self, request, response, num):
- num = sanitize(num)
- ids = generate_ids(num)
- ids.sort()
- worlds = []
- for item in ids:
- world = World[item]
- world.randomNumber = randint(1, 10000)
- worlds.append({"id": world.id, "randomNumber": world.randomNumber})
- add_ext_headers(response)
- response.media = worlds
- class Fortunes(object):
- _template = load_template()
- @session(serializable=False)
- def on_get(self, request, response):
- fortunes = [FortuneTuple(id=f.id, message=f.message) for f in Fortune.select()]
- fortunes.append(FortuneTuple(id=0, message="Additional fortune added at request time."))
- fortunes.sort(key=attrgetter("message"))
- content = self._template.render(fortunes=fortunes)
- add_ext_headers(response)
- response.content_type = falcon.MEDIA_HTML
- response.text = content
- class PlaintextResource(object):
- def on_get(self, request, response):
- add_ext_headers(response)
- response.content_type = falcon.MEDIA_TEXT
- response.text = 'Hello, world!'
- # register resources
- app.add_route("/json", JSONResource())
- app.add_route("/db", SingleQuery())
- app.add_route("/queries/{num}", MultipleQueries())
- app.add_route("/updates/{num}", UpdateQueries())
- app.add_route("/fortunes", Fortunes())
- app.add_route("/plaintext", PlaintextResource())
- # ------------------------------------------------------------------
- if __name__ == "__main__":
- import optparse
- import multiprocessing
- import logging
- import re
- parser = optparse.OptionParser("usage: %prog [options]", add_help_option=False)
- parser.add_option("-h", "--host", dest="host", default='0.0.0.0', type="string")
- parser.add_option("-p", "--port", dest="port", default=8080, type="int")
- parser.add_option("-s", "--server", dest="server", default="gunicorn", type="string")
- parser.add_option("-w", "--workers", dest="workers", default=0, type="int")
- parser.add_option("-k", "--keepalive", dest="keepalive", default=60, type="int")
- parser.add_option("-v", "--verbose", dest="verbose", default=0, type="int")
- (opt, args) = parser.parse_args()
- _is_travis = os.environ.get('TRAVIS') == 'true'
- workers = opt.workers
- if workers <= 0:
- workers = int(multiprocessing.cpu_count())
- if _is_travis:
- workers = 2
- if opt.server == 'waitress':
- import waitress
- response_server = None
- response_add_date = False
- logging.basicConfig()
- logging.getLogger().setLevel(logging.CRITICAL)
- logging.disable(True)
- if workers < 4:
- workers = 4
- waitress.serve(
- app=wsgi,
- listen=f"{opt.host}:{opt.port}",
- log_socket_errors=False,
- threads=workers,
- asyncore_use_poll=True,
- expose_tracebacks=False,
- connection_limit=128,
- channel_timeout=opt.keepalive,
- _quiet=True)
- sys.exit(0)
- def run_app():
- global response_server
- global response_add_date
-
- if opt.server == 'bjoern':
- import bjoern
- # Note:
- # Bjoern doesn't provide any additional response headers like Date and Server
- # so we need to provide them manually.
- bjoern_version = [i for i in open('requirements-bjoern.txt', 'r') if re.search('bjoern', i)][0].strip().split('==')
- response_server = '{}/{}'.format(*bjoern_version).title()
- response_add_date = True
- bjoern.run(app, host=opt.host, port=opt.port, reuse_port=True)
- if opt.server == 'fastwsgi':
- import fastwsgi
- response_server = "FastWSGI"
- response_add_date = False
- fastwsgi.server.backlog = 4096
- fastwsgi.run(app, host=opt.host, port=opt.port, loglevel=opt.verbose)
- if opt.server == 'socketify':
- import socketify
- response_server = None
- response_add_date = False
- socketify.WSGI(app).listen(opt.port, lambda config: logging.info(f"Listening on port http://localhost:{opt.port} now\n")).run()
- def create_fork():
- n = os.fork()
- # n greater than 0 means parent process
- if not n > 0:
- run_app()
- # fork limiting the cpu count - 1
- for i in range(1, workers):
- create_fork()
- run_app() # run app on the main process too :)
|