Why is my hosted Django app showing my username in the URL? - django

I've written a Django app which I'm trying to get set up on shared web hosting (A2). It's working, except that when I go to:
http://example.com/terms/
the URL changes in the browser bar to:
http://example.com/home/myusername/myappfolder/myappname/terms/
showing the full path to where my app is on disk.
This doesn't happen with static files - e.g. http://example.com.com/static/image.png works normally.
The app is running in a virtual environment. I'm using python 3.6.8 and Django 2.1.4.
I followed these instructions to set up my app, which include setting up this passenger.wsgi file, that looks like this:
import myapp.wsgi
SCRIPT_NAME = '/home/username/myapp'
class PassengerPathInfoFix(object):
"""
Sets PATH_INFO from REQUEST_URI because Passenger doesn't provide it.
"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
from urllib.parse import unquote
environ['SCRIPT_NAME'] = SCRIPT_NAME
request_uri = unquote(environ['REQUEST_URI'])
script_name = unquote(environ.get('SCRIPT_NAME', ''))
offset = request_uri.startswith(script_name) and len(environ['SCRIPT_NAME']) or 0
environ['PATH_INFO'] = request_uri[offset:].split('?', 1)[0]
return self.app(environ, start_response)
application = myapp.wsgi.application
application = PassengerPathInfoFix(application)
I'd be grateful for any pointers as to where to look to solve this.

Got it working!
In my modified passenger_wsgi.py, I changed the line
SCRIPT_NAME = os.getcwd()
to
SCRIPT_NAME = ''
One thing I should point out is that the absolute path was getting inserted on redirects - so if I visited
http://example.com/terms
it would redirect to
http://example.com/terms/
and insert the path in the URL.
As you're debugging I recommend disabling the cache, as that threw me for several loops when changes I made didn't seem to take effect.
Thanks to this question for getting me on the right track.

Related

Django 1.11 on passenger_wsgi not routing POST request

I'm trying to setup python on A2 shared hosting via passenger_wsgi.
The app is working fine when I run it via 'runserver'. I tested this both in my local PC, and via SSH tunnel.
However, when I try to set this up on passenger_wsgi, it can't seem to be able to route POST request.
1 import os
2 import sys
3
4 sys.path.insert(0, "/home/<username>/app")
5
6 import APP_CORE
7
8 # where is the python interpreter
9 INTERP = "/home/<username>/app/.virtualenv/bin/python"
10 if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)
11
12
13 os.environ['DJANGO_SETTINGS_MODULE'] = "APP_CORE.settings"
14
15 import APP_CORE.wsgi
16 application = APP_CORE.wsgi.application
Example: when I load the admin page (/admin/login), it can load the login page, but when submitting the credentials, it says that POST to /admin/login is not found - returning HTTP 404.
The SAME flow when I run via runserver works - I feel that I could be missing something in the django WSGI configuration. Any help would be appreciated !!
Edit/update: After diving into resolver.py and base.py:_get_response, I've noticed that somehow the /path/info truncates the first bit of the URL. Example, when I am requesting for /admin/login/, the path info only shows /login - but when I am using runserver, it is properly passed thru as /admin/login. To me this is clearly the issue on the web server setup and not on the django site. So will try to work it out with A2Hosting...
It looks like you may have solved this, but to followup for anyone that may stumble here. I have been using A2Hosting, Passenger, and CPanel with django (and wagtail). What I found was that during POST requests the wsgi SCRIPT_NAME was being set to a relative path and not the root of the application.
When I added logging to each application call, the correct GET request was:
{
'REQUEST_URI': '/admin/',
'PATH_INFO': '/admin/',
'SCRIPT_NAME': '',
'QUERY_STRING': '',
'REQUEST_METHOD': 'GET',
...
But on that page, a form was submitting a POST, which had the PATH_INFO incorrectly set:
{
'REQUEST_URI': '/admin/login/',
'PATH_INFO': '/login/',
'SCRIPT_NAME': '/admin',
'QUERY_STRING': '',
'REQUEST_METHOD': 'POST',
...
The workaround I ended up using was to create middleware which asserted a known SCRIPT_NAME and rebuilt the PATH_INFO from it.
# Set this to your root
SCRIPT_NAME = ''
class PassengerPathInfoFix(object):
"""
Sets PATH_INFO from REQUEST_URI since Passenger doesn't provide it.
"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
from urllib.parse import unquote
environ['SCRIPT_NAME'] = SCRIPT_NAME
request_uri = unquote(environ['REQUEST_URI'])
script_name = unquote(environ.get('SCRIPT_NAME', ''))
offset = request_uri.startswith(script_name) and len(environ['SCRIPT_NAME']) or 0
environ['PATH_INFO'] = request_uri[offset:].split('?', 1)[0]
return self.app(environ, start_response)
application = get_wsgi_application()
application = PassengerPathInfoFix(application)
Related Reading:
http://alyalearningdjango.blogspot.com/2014/05/issue-360-passenger-doesnt-set-pathinfo.html
https://github.com/phusion/passenger/issues/460
https://www.python.org/dev/peps/pep-0333/#environ-variables

Django ALLOWED_HOSTS to accept local IPs through Apache

I'm serving a Django app with Apache.
In Django's settings.py I have DEBUG = False, therefore I had to allow some hosts, like: ALLOWED_HOSTS = ['.dyndns.org', 'localhost']. This works fine, however I would like to have the server accessible on the local network via its internal IP address as well, like: 192.168.0.x, or 127.0.0.1, etc. How could I define 192.* or 127.* in ALLOWED_HOSTS, if I'd like to avoid opening up the access entirely by ALLOWED_HOSTS = ['*']?
Following the recommendation from #rnevius, and based on the guidelines from #AlvaroAV in how to setup custom middleware in django, I've managed to solve with this middleware:
from django.http import HttpResponseForbidden
class FilterHostMiddleware(object):
def process_request(self, request):
allowed_hosts = ['127.0.0.1', 'localhost'] # specify complete host names here
host = request.META.get('HTTP_HOST')
if host[len(host)-10:] == 'dyndns.org': # if the host ends with dyndns.org then add to the allowed hosts
allowed_hosts.append(host)
elif host[:7] == '192.168': # if the host starts with 192.168 then add to the allowed hosts
allowed_hosts.append(host)
if host not in allowed_hosts:
raise HttpResponseForbidden
return None
and setting ALLOWED_HOSTS = ['*'] in settings.py no longer opens up for all hosts in an uncontrolled way.
Thanks guys! :)
For those wondering what this should be in Django 2.0.dev (In line with #Zorgmorduk's answer)
You need to make the object callable: django middleware docs
Create a folder named middleware in yourproject/yourapp/
Create an empty file __init__.py inside yourproject/yourapp/middleware folder.
Create another file, in this case filter_host_middleware.py
Add this code inside filter_host_middleware.py:
from django.http import HttpResponseForbidden
class FilterHostMiddleware(object):
def __init__(self, process_request):
self.process_request = process_request
def __call__(self, request):
response = self.process_request(request)
return response
def process_request(self, request):`
# use the same process_request definition as in #Zorgmorduk's answer
add yourapp.middleware.filter_host_middleware.FilterHostMiddleware to your MIDDLEWARE in yourproject's settings.py; additionally change ALLOWED_HOSTS=['*']
You are all set!

Funny error using cherrypy wsgi server

I am serving my django project using cherrypy's wsgi server. The server serves all static and media files from django project. The code is the following
#create basic django WSGI app
app = WSGIHandler()
path = {'/': app}
#create app that handles static files from static root and put it in path
path[static_url] = StaticFileWsgiApplication(static_root)
#create app that handles /media and put it in path
path[media_url] = StaticFileWsgiApplication(media_root)
#dispatch the applications
dispatcher = wsgiserver.WSGIPathInfoDispatcher(path)
if __name__ == '__main__':
print 'Close window to exit'
#create wsgi cherrypy server
server = wsgiserver.CherryPyWSGIServer(
('0.0.0.0', 8000),
dispatcher,
server_name='cherrypy-django',
numthreads=20
)
try:
server.start()
except KeyboardInterrupt:
server.stop()
and the static file app
class StaticFileWsgiApplication(object):
#init function to get the directory of collectstatic (STATIC_ROOT)
def __init__(self, static_file_root):
self.static_file_root = os.path.normpath(static_file_root)
#Every wsgi app must be callable. It needs 2 arguments
#eviron: wsgi environ path
#start_response:Function creates the response
def __call__(self, environ, start_respone):
def done(status, headers, output):
#usefull for debugging
#returns the output(actual static file)
#also it produces the response using the start_response()
start_respone(status, headers.items())
return output
#get the path_info see PEP 333
path_info = environ['PATH_INFO']
#remove leading '/' from path (URI)
if path_info[0]=='/':
path_info = path_info[1:]
#actual file path in filesystem
file_path = os.path.normpath((os.path.join(self.static_file_root, path_info)))
#prevent escaping out of paths bellow media root (e.g via '..')
if not file_path.startswith(self.static_file_root):
status = '401 UNAUTHORIZED'
headers = {'Content_type':'text/plain'}
output = ['Permission denied. Illegal Path']
return done(status, headers, output)
#Only allow GET and HEAD requests not PUT, POST, DELETE
if not (environ['REQUEST_METHOD'] == 'GET' or environ['REQUEST_METHOD'] == 'HEAD'):
status = '405 METHOD NOT ALLOWED'
headers = {'Content_type':'text/plain'}
output = SimpleResponse(['405:Method not allowed'])
output.status_code = 405
return done(status, headers, output)
if not (os.path.exists(file_path)):
status = "404 NOT FOUND"
headers = {'Content_type':'text\plain'}
output = SimpleResponse(['Page not found %s' %file_path])
output.status_code = 404
return done(status, headers, output)
try:
fp = open(file_path, 'rb')
except IOError:
status = '401 UNAUTHORIZED'
headers = {'Content_type':'text/plain'}
output = SimpleResponse(['Permission denied %s' %file_path])
output.status_code = 401
return done(status, headers, output)
#This is a very simple implementation of conditional GET with
#the Last-Modified header. It makes media files a bit speedier
#because the files are only read off disk for the first request
#(assuming the browser/client supports conditional GET).
#mimetype needs to be ascii not uinicode, as django is all unicode, need to do conversion
mtime = http_date(os.stat(file_path)[stat.ST_MTIME]).encode('ascii','ignore')
if environ.get('HTTP_IF_MODIFIED_SINCE', None) == mtime:
headers = {'Last-Modified':mtime}
status = '304 NOT MODIFIED'
output = SimpleResponse()
output.status_code = 304
else:
status = '200 OK'
mime_type = mimetypes.guess_type(file_path)[0]
if mime_type:
headers = {'Content_type':mime_type}
output = BlockIteratorResponse(fp)
return done(status, headers,output)
I get an error in call about not initialising headers before using it...probably because all headers are inside if's. The error is
UnboundLocalError:Local variable "headers" referenced before assignment
The funny thing is that i get this error only on Mozilla but web works fine, everything is served correctly, and after i sign in to my webpage it doesn't show the error anymore.
Should I initialize headers variable in beginning of call like
headers = {}
And why is it happening only in firefox?
If you want to serve static content using CherryPy, you should use built-in tools -- see Serving Static Content. I guess you need to create a CherryPy app serving static content only and then combine it with your Django app with WSGIPathInfoDispatcher(...).
BUT what you should really consider is using nginx + uWSGI -- see Setting up Django and your web server with uWSGI and nginx.

Django hostname middleware gets cached

I created a Django project to manage two separate sites that share some backend code. Both of the sites are inside separate apps. Each app has its own models.py, views.py, templates etc...
To be able to react differently to different hostnames, I created an URLconf middleware:
class HostnameBasedUrlconfMiddleware(object):
"""This middleware parses the hostname from the request, and selects the
urlconf accordingly.
To set a custom urlconf according to the current hostname, add an URLCONF
dictionary to your settings.py file.
URLCONF = {
'example.com': 'urls_example',
'example.dev': 'urls_dev',
'admin.example.dev': 'apps.admin.urls'
}
If the hostname is not found in the URLCONF dictionary, the default
ROOT_URLCONF setting will be used.
"""
def process_request(self, request):
# Decide which urlconf to use. Fallback is to use the ROOT_URLCONF
# as defined in the settings.py file.
try:
hostname = request.META['HTTP_HOST']
request.urlconf = settings.URLCONF[hostname]
except (KeyError, AttributeError):
pass
return None
This seemed to work at first, but then I became aware that some kind of caching must be happening.
When starting the server and requesting site A, it would show up. If I then request site B, site A shows up. Sometimes (but not always), after several reloads, site B would finally show up. After restarting the server and requesting site B, it would show up, but now site A would show site B content.
This happened with the builtin devserver as well as with gunicorn.
I tried to request the site with curl to avoid browser caching, no difference.
I also suspected it could be some kind of template name collision, but all templates are inside a uniquely named subfolder inside their respective template folders.
I don't have memcached installed and I'm not using any caching middleware.
What could be the problem? Is there some internal automatic caching going on?
Here is the code in question that substitutes in the urlconf (for 1.3 at least):
django.core.handlers.base:
class BaseHandler(object):
[...snip...]
def get_response(self, request):
"Returns an HttpResponse object for the given HttpRequest"
from django.core import exceptions, urlresolvers
from django.conf import settings
try:
# Setup default url resolver for this thread, this code is outside
# the try/except so we don't get a spurious "unbound local
# variable" exception in the event an exception is raised before
# resolver is set
urlconf = settings.ROOT_URLCONF
urlresolvers.set_urlconf(urlconf)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
try:
response = None
# Apply request middleware
for middleware_method in self._request_middleware:
response = middleware_method(request)
if response:
break
if response is None:
if hasattr(request, "urlconf"):
# Reset url resolver with a custom urlconf.
urlconf = request.urlconf
urlresolvers.set_urlconf(urlconf)
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
[...snip...]
So, it looks like it's just using the value directly from request.urlconf. And your middleware is setting the request value directly.
I'd install django-debug-toolbar to confirm whether or not the value for request.urlconf is a) being set or b) being changed along the way.
To make absolutely sure, why not change the code temporarily to something like:
request.urlconf = settings.URLCONF[hostname]
request.urlconf_set = datetime.datetime.now()
Then you can look at the values in the debug toolbar (or just output them in a template) to see what might be going on.
However, I would suggest instead of using middleware, that you simply set up different settings.py files for each domain. Then, in whatever web server you're using, set each one up to use its own .wsgi file, which points to its own settings file, like so:
settings_a.py:
from settings import *
ROOT_URLCONF = 'urls_a.py'
settings_b.py
from settings import *
ROOT_URLCONF = 'urls_b.py'

Twisted hangs while daemonize

I'm beginner in twisted world, so first I'm trying to get my working django project configured under twisted,currently its working well on django testing server or apache via mod_wsgi.
I followed this link and this too to configure the setup, based on that I have a server.py file given bellow
So in-order to integrate django app with twisted I used the following code,
import sys
import os
from twisted.application import internet, service
from twisted.web import server, resource, wsgi, static
from twisted.python import threadpool
from twisted.internet import reactor
from django.conf import settings
import twresource # This file hold implementation of "Class Root".
class ThreadPoolService(service.Service):
def __init__(self, pool):
self.pool = pool
def startService(self):
service.Service.startService(self)
self.pool.start()
def stopService(self):
service.Service.stopService(self)
self.pool.stop()
class Root(resource.Resource):
def __init__(self, wsgi_resource):
resource.Resource.__init__(self)
self.wsgi_resource = wsgi_resource
def getChild(self, path, request):
path0 = request.prepath.pop(0)
request.postpath.insert(0, path0)
return self.wsgi_resource
PORT = 8080
# Environment setup for your Django project files:
#insert it to first so our project will get first priority.
sys.path.insert(0,"django_project")
sys.path.insert(0,".")
os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project.settings'
from django.core.handlers.wsgi import WSGIHandler
def wsgi_resource():
pool = threadpool.ThreadPool()
pool.start()
# Allow Ctrl-C to get you out cleanly:
reactor.addSystemEventTrigger('after', 'shutdown', pool.stop)
wsgi_resource = wsgi.WSGIResource(reactor, pool, WSGIHandler())
return wsgi_resource
# Twisted Application Framework setup:
application = service.Application('twisted-django')
# WSGI container for Django, combine it with twisted.web.Resource:
# XXX this is the only 'ugly' part: see the 'getChild' method in twresource.Root
wsgi_root = wsgi_resource()
root = Root(wsgi_root)
#multi = service.MultiService()
#pool = threadpool.ThreadPool()
#tps = ThreadPoolService(pool)
#tps.setServiceParent(multi)
#resource = wsgi.WSGIResource(reactor, tps.pool, WSGIHandler())
#root = twresource.Root(resource)
#Admin Site media files
#staticrsrc = static.File(os.path.join(os.path.abspath("."), "/usr/haridas/eclipse_workplace/skgargpms/django/contrib/admin/media/"))
#root.putChild("admin/media", staticrsrc)
# Serve it up:
main_site = server.Site(root)
#internet.TCPServer(PORT, main_site).setServiceParent(multi)
internet.TCPServer(PORT, main_site).setServiceParent(application)
#EOF.
Using above code It worked well from command line using "twisted -ny server.py", but when we run it as daemon "twisted -y server.py" it will hang, but the app is listening to the port 8080. I can access it using telnet.
I found some fixes for this hanging issue from stackoverflow itself. It helped me to use the code sections given below, which is commented in the above server.py file.
multi = service.MultiService()
pool = threadpool.ThreadPool()
tps = ThreadPoolService(pool)
tps.setServiceParent(multi)
resource = wsgi.WSGIResource(reactor, tps.pool, WSGIHandler())
root = twresource.Root(resource)
and :-
internet.TCPServer(PORT, main_site).setServiceParent(multi)
instead of using the:-
wsgi_root = wsgi_resource()
root = Root(wsgi_root)
and :-
internet.TCPServer(PORT, main_site).setServiceParent(application)
The modified method also didn't helped me to avoid the hanging issue.Is any body out there who successfully run the django apps under twisted daemon mode?.
I maid any mistakes while combining these codes?, Currently I'm only started to learn the twisted architectures in detail. Please help me to solve this problem
I'm looking for the Twisted Application configuration (TAC) file, which integrate django app with twisted and run with out any problem in the daemon mode also.
Thanks and Regards,
Haridas N.
I think you are almost there. Just add one more line at the very end:
multi.setServiceParent(application)