I can’t get bottle to match any other route than “/” when deploying in a CGI environment. (I’m stuck with the hosting provider, FastCGI or WSGI are not on offer, unfortunately).
Bottle lives in a subdirectory lib
– I have dropped the bottle.py from bottle-0.12.18.tar.gz there.
Python is either 3.5.3 (provider) or 3.8.2 (localhost)
I have the following in a .htaccess
Options +ExecCGI
AddHandler cgi-script .py
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^static/ - [L]
RewriteRule .* application.py [L]
</IfModule>
and the following application.py
#!/usr/bin/python3
#setup lib path
import os
import os.path
import sys
if 'SCRIPT_NAME' in os.environ:
MY_DIR = os.path.dirname(os.path.realpath(os.environ['SCRIPT_FILENAME']))
else:
MY_DIR = os.environ['PWD']
sys.path.append(os.path.join(MY_DIR,'lib'))
from bottle import Bottle
app = Bottle()
@app.route('/test')
def test():
return '<b>matched @app.route("/test") - Testing: One, Two</b>!'
@app.route('/')
def index():
r = ""
for p in os.environ.keys():
r += "{0} : {1}n".format(p,os.environ[p])
return '<b>matched @app.route("/")</b>n<pre>'+r+'</pre>'
app.run(server='cgi')
This always return the output from index()
, no matter what URL I request.
From the output
matched @app.route("/")
APP_ENGINE : phpcgi
APP_ENGINE_VERSION : 7.3
AUTH_TYPE : Basic
CFG_CLUSTER : cluster003
DOCUMENT_ROOT : /home/somethinguxiz/www-dev
ENVIRONMENT : production
GATEWAY_INTERFACE : CGI/1.1
HOME : /homez.907/somethinguxiz
HTTP_ACCEPT_ENCODING : gzip, deflate, br
HTTP_ACCEPT_LANGUAGE : en,de-DE;q=0.7,de;q=0.3
HTTP_ACCEPT : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
HTTP_DNT : 1
HTTP_FORWARDED : for=51.xxx.xxx.xxx; proto=https; host=dev.something.else
HTTP_HOST : dev.something.else
HTTP_REMOTE_IP : 90.xxx.xxx.xxx
HTTPS : on
HTTP_UPGRADE_INSECURE_REQUESTS : 1
HTTP_USER_AGENT : Mozilla/5.0 (X11; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0
HTTP_X_FORWARDED_FOR : 90.xxx.xxx.xxx
HTTP_X_FORWARDED_PORT : 443
HTTP_X_FORWARDED_PROTO : https
HTTP_X_PREDICTOR : 1
HTTP_X_REMOTE_IP : 51.xxx.xxx.xxx
HTTP_X_REMOTE_PORT : 64440
HTTP_X_REMOTE_PROTO : https
PATH : /usr/local/bin:/usr/bin:/bin
PHP_VER : 5_TEST
PWD : /homez.907/somethinguxiz/www-dev
QUERY_STRING :
REDIRECT_STATUS : 200
REDIRECT_URL : /test
REGISTER_GLOBALS : 0
REMOTE_ADDR : 90.xxx.xxx.xxx
REMOTE_PORT : 17926
REMOTE_USER : csomething
REQUEST_METHOD : GET
REQUEST_URI : /test
SCRIPT_FILENAME : /home/somethinguxiz/www-dev/application.py
SCRIPT_NAME : /application.py
SCRIPT_URI : https://dev.something.else:443/test
SCRIPT_URL : /test
SERVER_ADDR : 51.xxx.xxx.xxx
SERVER_ADMIN : [email protected]
SERVER_NAME : dev.something.else
SERVER_PORT : 443
SERVER_PROTOCOL : HTTP/1.1
SERVER_SIGNATURE :
SERVER_SOFTWARE : Apache
UID : somethinguxiz
I.e. the script does see the REQUEST_URI
yet it does not fire test()
but index()
!?
Any ideas, pointers?
EDIT:
unless it is removed somewhere, PATH_INFO
is missing in the above list although it should be there if I understand the CGI specs correctly. It’s also what bottle uses in its match() method. It falls back to /
if not present (which explains that always index()
is called.
EDIT: move solution text to answer
2
Answers
PATH_INFO
is only set if the path goes beyond the cgi script. While my original intent was to have that invisible I have made peace with having an application name here. Plus, there is a difference in having[L]
in the rewrite rule compared to[END]
.Thus I settled with the following
.htaccess
:It allows to serve static content from httpd while application logic is in
application.py
- hidden asdemo
. Anything else redirects to the front door.Application is at
.../demo
, Pages within the application are at.../demo/path/to/page
, and Ressources served by httpd are at.../demo/static/path/to/ressource
I had this problem too, and found a solution that let me completely eliminate any path leftovers for the script itself. The following Apache Rewrite directives in the .htaccess file in the CGI script’s directory did the trick:
Explanations of the numbered lines:
/<non-empty-request>
will run as if it was/script.py/<non-empty-request>
.Thus, 1st line’s Rule sending
https://www.example.com
to my script lets its@app.route('/')
handler do its job properly regardless of static index files, and the 4th line’s Rule makes sure all other requests for non-static files are properly fed to the script, and are handled by all other registered route handlers.As a side note, that
RewriteBase /
isn’t strictly necessary, but I like to have that default made explicit. See this answer to How does RewriteBase work in .htaccess for a thorough breakdown ofRewriteBase
and what it’s good for.