-
-
Save jjmontesl/203ac28b6690fb70de67818cb4d6f149 to your computer and use it in GitHub Desktop.
import logging | |
import asyncio | |
from cf.comp.component import Component | |
from quart import Quart, websocket, app, send_from_directory, redirect, request, url_for | |
from quart.serving import run_app, Server | |
from django.core.servers.basehttp import ( | |
WSGIServer, get_internal_wsgi_application, run, | |
) | |
from django.core.handlers.wsgi import WSGIRequest | |
from cf.core.injection import Inject | |
from cf_django.django import DjangoService | |
import os | |
from http.cookies import SimpleCookie | |
import pdb | |
logger = logging.getLogger(__name__) | |
logger_quart = logging.getLogger("quart") | |
class DjangoHTTPService(Component): | |
''' | |
''' | |
http = None | |
django = Inject(DjangoService) | |
async def initialize(self): | |
logger.info("Starting Django HTTP bridge: %s", os.environ['DJANGO_SETTINGS_MODULE']) | |
self.handler = get_internal_wsgi_application() | |
#run(self.addr, int(self.port), handler, | |
# ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls) | |
@self.http.app.route('/<path:path>', methods=['GET', 'POST']) | |
async def cf_django(path): | |
headers = request.headers | |
data = await request.data | |
values = await request.values | |
body = await request.body | |
# As per: https://github.com/django/django/blob/master/django/core/handlers/wsgi.py | |
# and: https://www.python.org/dev/peps/pep-0333/ | |
environ = {} | |
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' | |
environ['SCRIPT_NAME'] = '' | |
environ['REQUEST_METHOD'] = request.method.upper() | |
environ['PATH_INFO'] = '/' + path if path else '/' | |
environ['SERVER_NAME'] = "localhost" | |
environ['SERVER_PORT'] = 8000 | |
if request.content_type: | |
environ['CONTENT_TYPE'] = request.content_type | |
if request.cookies: | |
cookies = SimpleCookie({k: v for k, v in request.cookies.items()}) | |
environ['HTTP_COOKIE'] = str(cookies).split(" ", 1)[1] | |
if headers: | |
for k, v in headers.items(): | |
environ[k] = v | |
if data: | |
environ['QUERY_STRING'] = str(data) | |
environ['wsgi.input'] = body | |
logger.debug("Django Request: %s", environ) | |
logger.debug(headers) | |
logger.debug(data) | |
django_request = WSGIRequest(environ) | |
response = self.handler.get_response(django_request) | |
logger.debug(response) | |
#pdb.set_trace() | |
logger.debug(response.cookies) | |
resp_headers = {} | |
if response.cookies: | |
resp_headers['Set-Cookie'] = str(response.cookies).split(' ', 1)[1] | |
return (response, response.status_code, resp_headers) | |
@self.http.app.route('/', methods=['GET', 'POST']) | |
async def cf_django_root(): | |
return await cf_django(None) | |
import logging | |
import asyncio | |
from cf.comp.component import Component | |
from quart import Quart, websocket, app, send_from_directory, redirect, request, url_for | |
from quart.serving import run_app, Server | |
import os | |
logger = logging.getLogger(__name__) | |
class DjangoService(Component): | |
''' | |
''' | |
settings = None | |
async def initialize(self): | |
logger.debug("Initializing Django (settings=%s)", self.settings) | |
os.environ['DJANGO_SETTINGS_MODULE'] = self.settings | |
from django.conf import settings | |
self._settings = settings | |
#settings.configure() | |
import logging | |
import asyncio | |
from cf.comp.component import Component | |
from quart import Quart, websocket, app, send_from_directory | |
from quart.serving import run_app, Server | |
logger = logging.getLogger(__name__) | |
logger_quart = logging.getLogger("quart") | |
class HTTPService(Component): | |
''' | |
''' | |
host = "127.0.0.1" | |
port = 8000 | |
app = None | |
async def initialize(self): | |
logger.info("Starting HTTP service [host=%s, port=%s]", self.host, self.port) | |
self.app = Quart("olass") | |
''' | |
app.run(host=self.host, | |
port=self.port, | |
debug=self.ctx.olass.debug, | |
access_log_format="%(h)s %(r)s %(s)s %(b)s %(D)s", | |
keep_alive_timeout=5, | |
#use_reloader: bool=False, | |
loop=self.ctx.olass.loop) | |
''' | |
#@app.route('/') | |
#async def hello(): | |
# return 'hello' | |
#@self.app.route('/ui/<path:path>') | |
#async def ui_static(path): | |
# return await send_from_directory('olass/webui/static', path) | |
#@self.app.websocket('/ws') | |
#async def ws(): | |
# while True: | |
# await websocket.send('Test message') | |
# await asyncio.sleep(5) | |
# Create the asyncio server | |
await self.ctx.cf.loop.create_server( | |
lambda: Server(self.app, self.ctx.cf.loop, logger_quart, "HTTP: %(h)s %(r)s %(s)s %(b)s %(D)s", 5), | |
self.host, self.port, ssl=None) | |
Remember, when using Django for a service, that model instances lifecycle is usually not a problem because objects live only as long as the HTTPRequest that creates them. Within a service, this doesn't happen, and you need to synchronize access to model instances carefully. This involves avoiding usage of cached/older instances to do modifications, and refreshing model objects anytime other process might have changed them. Django CRM does not guarantee uniqueness of model objects that represent the same database record.
Also note that you can use a separate process if you are using Django Requests (HTTP) just to serve a few preconfigured templates, static views, admin views, or in general, stuff that is backed by the database (because the database serves a truth source). You only need the kind of integration discussed in this gist if you need to access the realtime in-memory state of your process from Django views and templates. If you can avoid it, an alternate approach is to provide a static web application and let the HTTP clients to use websockets to communicate with your process (this is something I'm actually using in production for another project and works well as Django is merely serving static and admin views, which only hit the database). I this case I prefer to use WAMP and a WAMP router (crossbar.io) to handle websockets, instead of listening to websockets directly from your application.
This is experimental, not finished code and it has several known issues. Nor I am convinced that integrating via WSGI is the best option here.
This is an excerpt of a larger project and it's using features not shown here (dependency injection, configuration injection, lifecycle management...).
I have uploaded this gist to complement the SO question: https://stackoverflow.com/questions/51252760/how-to-integrate-django-app-with-tcp-server