Here’s a demonstration of a Python web stack running next to the current PHP stack, such that it’s invisible to the client. This approach aims at providing a way to migrate the PHP code base to Python, one endpoint after another, with as little glue as possible to have the two backends collaborate. Since they are both mounted on the web root, Python could implement, say, /packages/ and leave the rest to PHP. As soon as PHP yields it by returning 404 on this URL, Python will take over automatically. To run it, you need python-flask and nginx. Then, you need to start PHP, Flask, and nginx, in whatever order: $ cd path/to/aurweb $ AUR_CONFIG="$PWD/conf/config" php -S 127.0.0.1:8080 -t web/html $ FLASK_APP=aurweb.wsgi flask run $ nginx -p . -c conf/nginx.conf You may then open http://localhost:8081/ and http://localhost:8081/hello to check that the former URL goes to PHP and the latter to Flask. The key concept is nginx’s proxy_next_upstream feature. We set Flask as a fallback backend, and tell nginx to use the fallback only on 404 from PHP. See http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream The main limitation of this approach is that PHP and Python need to use the same gateway protocol, probably FastCGI or HTTP. A minor caveat with this system is that the body of the 404 returned by PHP is lost, though it could contain useful information like “Package X doesn’t exist”, rather than a generic “Page not found”. Luckily, all the 404 cases are handled by 404.php, so we could port its logic to Flask and preserve the current behavior. --- aurweb/wsgi.py | 15 +++++++++++++++ conf/nginx.conf | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 aurweb/wsgi.py create mode 100644 conf/nginx.conf diff --git a/aurweb/wsgi.py b/aurweb/wsgi.py new file mode 100644 index 00000000..fd6b67d3 --- /dev/null +++ b/aurweb/wsgi.py @@ -0,0 +1,15 @@ +from flask import Flask, request + + +def create_app(): + app = Flask(__name__) + + @app.route('/hello', methods=['GET', 'POST']) + def hello(): + return ( + f"{request.method} {request.url}\n" + f"{request.headers}" + f"{request.get_data(as_text=True)}\n" + ), {'Content-Type': 'text/plain'} + + return app diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 index 00000000..8e6e4edb --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,23 @@ +events { +} + +daemon off; +error_log /dev/stderr info; +pid nginx.pid; + +http { + access_log /dev/stdout; + + upstream aurweb { + server [::1]:8080 max_fails=0; + server 127.0.0.1:5000 backup max_fails=0; + } + + server { + listen 8081; + location / { + proxy_pass http://aurweb; + proxy_next_upstream http_404 non_idempotent; + } + } +} -- 2.25.1