Project

General

Profile

Actions

Bug #2039

closed

Wrong handling of PATH_INFO for FCGI/SCGI (1.5.x)

Added by peto over 15 years ago. Updated over 8 years ago.

Status:
Obsolete
Priority:
Normal
Category:
mod_fastcgi
Target version:
ASK QUESTIONS IN Forums:

Description

I'm trying to set up a Django backend behind Lighttpd. It doesn't pick up the script URLs correctly; it sees everything as "/", because PATH_INFO is always empty.

Django resolves the request based soley on PATH_INFO. This is consistent with the basic definition of PATH_INFO: "The PATH_INFO variable specifies a path to be interpreted by the CGI script." (RFC 3875 4.1.5.) I think Django is doing the right thing here.

The basic problem is that the CGI spec was written back when a CGI request always referenced a real file, like "http://example.com/site.cgi/page". The front-end always had a real file in the URL (SCRIPT_NAME), and PATH_INFO ("/page") was everything after that. With a modern FCGI backend, the URL doesn't reference a real file at all. So, what you're supposed to do here--what goes in SCRIPT_NAME, and what in PATH_INFO--is poorly-defined.

I think the correct fix, where there's no con->physical.doc_root, is to move the path from SCRIPT_NAME to PATH_INFO. For example, currently:

REQUEST_URI=/user/login
SCRIPT_NAME=/user/login
PATH_INFO=

Instead, when con->request.pathinfo is empty,

REQUEST_URI=/user/login
SCRIPT_NAME=
PATH_INFO=/user/login

Effects:

- This makes Django happy, because it's looking for the URL it's supposed to be serving in PATH_INFO. Again, I think Django is doing the right thing here.
- SCRIPT_NAME must not still be /user/login; RFC 3875 4.1.13 is very clear on this. (Also, if it is, it breaks Django again, for unrelated reasons.)
- Rails is unaffected, because it only uses REQUEST_URI (untested, not using Rails on this system).
- PHP is unaffected; it stupid and uses its own header anyway.

Does this honor or violate 4.1.13 "The SCRIPT_NAME variable MUST be set to a URI path (not URL-encoded) which could identify the CGI script"? Who knows--the definition makes no sense, because there's no actual "CGI script". I think this arguably honors it: conceptually, the "CGI script" in a dynamic backend is the whole backend itself, so the identifying name for the script in "http://example.com/auth/login" is ""--the (lack of) stuff between the hostname and PATH_INFO.

The attached patch for trunk has been tested with Django in both FCGI and SCGI. This means it's only been tested in the no-request.pathinfo code path. I don't know when the other code path is even used, since FastCGI seems to take over from handle_get_backend before the pathinfo case is even hit.


Files

Actions #1

Updated by peto over 15 years ago

(Left some debug cruft in the patch by accident, by the way.)

Actions #2

Updated by stbuehler over 15 years ago

Is there a really good reason why you don't just use proxy-core.rewrite-request for this?

And you assume that all FastCGI applications live in "/" if i understood that correctly.

The other code path is used when you put the proxy-core backend in a $PHYSICAL["existing-path"] =~ "\.php$" conditional.

Actions #3

Updated by stbuehler over 15 years ago

  • Target version changed from 6 to 1.5.0
Actions #4

Updated by peto over 15 years ago

Is there a really good reason why you don't just use proxy-core.rewrite-request for this?

Because this is a bug. Is there a really good reason you want to leave a bug in place and force every Django user (and probably others) to use rewriting hacks to work around it?

The other code path is used when you put the proxy-core backend in a $PHYSICAL["existing-path"] =~ "\.php$" conditional.

Here's a more consistent fix: choose based on whether con->physical refers to a file that actually exists or not. If it's a real file, that path clearly belongs in SCRIPT_NAME. If not, then leave SCRIPT_NAME blank as above.

http://example.com/forum/index.php/1/2/3
con->physical = /var/www/forum/index.php exists
SCRIPT_NAME = /forum/index.php
PATH_INFO = /1/2/3

http://example.com/joe/blog/1/2/2
con->physical = /var/www/joe/blog/1/2/3, which does not actually exist
SCRIPT_NAME = ""
PATH_INFO = /joe/blog/1/2/2

This gives the same results for Django as the current patch, and gives a correct SCRIPT_NAME/PATH_INFO for PHP-like stuff.

(Maybe you have a Django app that doesn't live in "/" and you want SCRIPT_NAME set to its prefix, but that's a separate issue--that'd need a new configuration setting.)

Actions #5

Updated by stbuehler over 15 years ago

But maybe it isn't the job of the webserver to decide for itself if the file exists; the FastCGI application maybe remote (or the directories/files not readable).

And:

(Maybe you have a Django app that doesn't live in "/" and you want SCRIPT_NAME set to its prefix, but that's a separate issue--that'd need a new configuration setting.)

There is a configuration setting... proxy-core.rewrite-request.

Actions #6

Updated by peto over 15 years ago

If the file is remote, or inaccessible to the frontend, or doesn't exist as a real file at all, that means it's entirely the FCGI app's job to interpret the path.

The part of the path that's the FCGI app's job to interpret is stored in PATH_NAME. (RFC 3875 4.1.5.)

The portion of the URL that goes in PATH_NAME must not go in SCRIPT_NAME. (RFC 3875 4.1.13.)

There is a configuration setting... proxy-core.rewrite-request.

Using this for the basic case to work around Lighttpd not doing the above correctly is a hack.

Actions #7

Updated by stbuehler over 15 years ago

RFC 3875 4.1.13.

The SCRIPT_NAME string forms some leading part of the path component
of the Script-URI derived in some implementation-defined manner.

So our implementation differs from what you would like to have implemented.
I don't say your way is wrong, i just don't think the current behaviour is buggy and i don't like doing unnecessary changes.

Actions #8

Updated by peto over 15 years ago

I explain precisely why something is wrong, and why it causes real-world problems (breaking Django), and provide a patch, and you simply refuse to fix it with no explanation. You say how much you hate hacks, yet you have no hesitation in insisting that users use ugly hacks to work around bugs that you--inexplicably--refuse to fix.

I'm done. Reporting bugs here is a waste of my time and there are better places I can expend my energy and patience.

Actions #9

Updated by nitrox over 15 years ago

Hm..stupid situation, both are pissed. Though i can ensure you (peto) Jan and stbuehler discussed this bug behind the scene for quite some time. I´m no expert on this, so i can´t say who´s right or wrong :-)

"I'm done. Reporting bugs here is a waste of my time and there are better places I can expend my energy and patience." - That would be very sad. I hope you don´t. But that was kind of offending to stbuehler, because he did spent some time on this too.

You´re always welcome to discuss things like this on #lighttpd at irc.freenode.net, and we update this and other threads afterwards for documentation.

Actions #10

Updated by gstrauss over 8 years ago

  • Category set to mod_fastcgi
Actions #11

Updated by gstrauss over 8 years ago

  • Missing in 1.5.x set to Yes

This is unequivocally a bug if SCRIPT_NAME duplicates PATH_INFO. As peto wrote:

The portion of the URL that goes in PATH_NAME (sic) must not go in SCRIPT_NAME. (RFC 3875 4.1.13.)

However, the 1.5.x branch has been abandoned. (See top commit by stbuehler in branch lighttpd-1.5.x)

On the other hand, there are indications that the issue raised in this ticket is not an issue in the lighttpd 1.4.x branch. The official documentation provides instructions how to set up Django with lighttpd: https://docs.djangoproject.com/en/1.8/howto/deployment/fastcgi/

Note that the above page also warns:

Deprecated since version 1.7: FastCGI support is deprecated and will be removed in Django 1.9.

If the issue raised in this ticket still affects the lighttpd 1.4.x branch, please note that here or in a new ticket, and I will evaluate and fix it. Thanks.

Actions #12

Updated by gstrauss over 8 years ago

  • Status changed from New to Obsolete
Actions

Also available in: Atom