Re: runserver ignoring SCRIPT_NAME?

461 views
Skip to first unread message

Shai Berger

unread,
Oct 13, 2013, 10:49:58 AM10/13/13
to django-d...@googlegroups.com
Hi JK,

On Thursday 10 October 2013 15:16:41 JK Laiho wrote:
>
> I'm developing a Django site that will be served in production by
> nginx-proxied gunicorn. The site is mounted on a subpath, and the nginx
> config sets SCRIPT_NAME to /path to facilitate this.
>
> This arrangement works fine with gunicorn, which I've got set up in my
> Vagrant environment, but usually I like to use runserver or runserver_plus
> from django-extensions during development. From nginx's perspective, there
> is no difference, as both gunicorn and runserver serve at 127.0.0.1:8000.
> However, while the site mounts correctly to /path under gunicorn, this is
> not the case with runserver. I looked into why this is, and after some time
> spent in pdb discovered what looks to be the reason.
>
> [...]
>
> Should I file a ticket for this?
>
The "runserver" command is intended to be used as a front-facing server, and
not as a backend for an external server. While it may be used that way, this
use is not intended. The few days that have gone without any response for your
request are an indicator that there is little interest in this in the
community.

So, if this can be arranged with a trivial fix, such a fix may be accepted, but
if there's any cost involved (in complication and/or risk), the issue is very
likely to be wontfix'd; it is even possible that it will be wontfix'd on the
grounds that fixing it may encourage the use of runserver in production, which
we strongly recommend against.

You can write your own WSGIRequestHandler and use that instead of Django's, by
modifying the wsgi file (which is a project file). You can even inherit Django's
handler. If you need modifications in Django's handler in order to extend it
the way you want, that may be received more favorably then a straight attempt
to make runserver behave well behind nginx.

Hope this helps,
Shai.


Aymeric Augustin

unread,
Oct 13, 2013, 12:40:21 PM10/13/13
to django-d...@googlegroups.com
This ticket may be related:

https://code.djangoproject.com/ticket/7930

--
Aymeric.

Noah Kantrowitz

unread,
Oct 13, 2013, 2:20:00 PM10/13/13
to django-d...@googlegroups.com, JK Laiho
SCRIPT_NAME is not an HTTP header, it cannot be sent from nginx to your runserver since it is only part of the CGI/WSGI environment, not the headers. That said you could still add a command line option to set SCRIPT_NAME within runserver, but that's slightly different than what you described I think.

--Noah

JK Laiho <jkl...@iki.fi> wrote:
Hi all,

I'm developing a Django site that will be served in production by nginx-proxied gunicorn. The site is mounted on a subpath, and the nginx config sets SCRIPT_NAME to /path to facilitate this.

This arrangement works fine with gunicorn, which I've got set up in my Vagrant environment, but usually I like to use runserver or runserver_plus from django-extensions during development. From nginx's perspective, there is no difference, as both gunicorn and runserver serve at 127.0.0.1:8000. However, while the site mounts correctly to /path under gunicorn, this is not the case with runserver. I looked into why this is, and after some time spent in pdb discovered what looks to be the reason.

Python's simple_server.WSGIServer sets SCRIPT_NAME to '' in its setup_environ method. Later, simple_server.WSGIRequestHandler does the actual header processing in its get_environ method. The culprit is this for loop contained within:

for h in self.headers.headers:
    k,v = h.split(':',1)
    k=k.replace('-','_').upper(); v=v.strip()
    if k in env:
        continue                    # skip content length, type,etc.
    if 'HTTP_'+k in env:
        env['HTTP_'+k] += ','+v     # comma-separate multiple headers
    else:
        env['HTTP_'+k] = v

Since SCRIPT_NAME is already in env as an empty string, the first if clause is evaluated and the nginx-provided SCRIPT_NAME is never added to the environment, causing things to fail down the line. Later, in the else clause, SCRIPT_NAME becomes HTTP_SCRIPT_NAME, so it is ultimately available to Django, but get_script_name in django.core.handlers.base (in 1.6b4, which I'm using; .wsgi in the development version) does not take it into consideration.

At a glance, get_script_name seems like a logical place for a quick fix, but I'm unable to see all the implications of, say, looking at HTTP_SCRIPT_NAME there in addition to SCRIPT_NAME. To a layman, it's also conceivable that Django's WSGIRequestHandler could extend the get_environ method of its base class and add some special handling for SCRIPT_NAME there. I'm not qualified to present either option as a recommendation, though.

Should I file a ticket for this?

- JK

JK Laiho

unread,
Oct 14, 2013, 2:06:06 AM10/14/13
to django-d...@googlegroups.com
Thanks for your replies. Seeing as this seems to be quite specific to my current setup, I'll just try to solve this for my use case by replacing or wrapping one of the components that make up the machinery behind the runserver command. If the result is worth publicizing, I might put it up on Github; a Django ticket is probably be the wrong way to approach this.

- JK
Reply all
Reply to author
Forward
0 new messages