Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Ticket #613 (defect)

Opened 2 years ago

Last modified 2 years ago

HTTPRedirect from default handler goes up one directory.

Status: closed (fixed)

Reported by: Ethilien Assigned to: rdelon
Priority: normal Milestone: 3.0
Component: CherryPy code Keywords:
Cc:

I am having a problem with HTTPRedirects from a default handler. Here's my setup. I have a class called StudentController which is sitting on my root handler under 'students'. All requests are going to a default handler, which under certain circumstances will redirect to another page which is also handled by this default handler.

class StudentController:
    
    def default(self, *vpath):
        raise cherrypy.HTTPRedirect("contact")
    default.exposed = True

app.root.students = StudentController()

The problem is that when I redirect, it takes me back to the root of the site.

For instance, the user first requests /students/. This raises an HTTPRedirect to 'contact'. However, instead of taking the user to /students/contact like I would expect it to, they are redirected to /contact. I have tracked this problem down to the url function in __init__.py. Apparently this function will automatically strip a trailing slash from the current url if the call is not being handled by an index function, which it isn't because it's a default handler. This causes the result of cherrypy.url (which is called in the HTTPRedirect __init__ function) to return /students (note the lack of a trailing slash). _urljoin then removes the 'students' part of the url, and the resulting url is /contact.

Attachments

is_dir.patch (3.0 kB) - added by Ethilien on 12/01/06 14:33:41.
Patch file for this bug

Change History

12/01/06 14:33:41: Modified by Ethilien

  • attachment is_dir.patch added.

Patch file for this bug

12/01/06 14:36:24: Modified by Ethilien

  • milestone deleted.

The patch does two things, it renames the is_index attribute of the request object to is_dir, and causes it to be set to true for both index and default handlers. I renamed this attribute because I thought it would be more appropriate than just setting is_index to true for a default handler.

12/01/06 15:19:02: Modified by lawouach

Have you tried this:

12/01/06 15:19:43: Modified by lawouach

oops it did not work

import cherrypy

class Root:
    @cherrypy.expose
    def default(self, path=None):
        if not path:
            raise cherrypy.HTTPRedirect(cherrypy.url('/some'))
        return path
    
if __name__ == '__main__':
    cherrypy.quickstart(Root(), '/test')

12/01/06 17:55:39: Modified by guest

True, that works, but this doesn't unfortunately:

import cherrypy

class Root:
    def __init__(self):
        self.test = Test()

class Test:
    @cherrypy.expose
    def default(self, path=None):
        if not path:
            raise cherrypy.HTTPRedirect(cherrypy.url('/some'))
        return path
    
if __name__ == '__main__':
    cherrypy.quickstart(Root())

Your testcase mounted the default handler on the base (the scriptname) of the site, which caused cherrypy.url to return the new url properly. Mine isn't setup that way as I have several controller classes mounted on the root of the site, so I can't put them each as the scriptname.

12/01/06 18:18:24: Modified by lawouach

In that case I'd say CP's behavior is correct and as expected. I think you understand that mounting the application as you do does not tell CP that this is a distinct application. This is precisely where the subtlety resides.

In your example as you say you mount your controllers on the root application and they CherryPy treats them as such. Controllers.

If you want them to behave as applications do, you have to mount them via cherrypy.tree.mount.

Maybe with something like:

import cherrypy

class Root:
    def __init__(self, base=''):
        cherrypy.tree.mount(Test(), "%s/test" % base)

class Test:
    @cherrypy.expose
    def default(self, path=None):
        if not path:
            raise cherrypy.HTTPRedirect(cherrypy.url('/some'))
        return path
    
if __name__ == '__main__':
    cherrypy.quickstart(Root())

12/01/06 18:45:56: Modified by Ethilien

Ok, maybe I just don't understand the role of a default controller. The way I understand cherrypy's routing methods (which may be wrong, I am very new at this) is that classes represent directories, and functions represent files. So since the default handler is a function, it should be considered a file under a directory, and be able to redirect to other files in that directory.

For instance, if I redirect from the index handler I would get a file in that directory. So if I have an index handler for /students/, which redirects to 'contact', I would get /students/contact, but not for a default handler. This behavior seems inconsistent to me, but that could be because of a lack of understanding on my part.

Also, in my application the separate classes are not different apps, they are just different parts of the same app.

Thanks for helping me understand this, Ethilien

12/01/06 18:52:53: Modified by Ethilien

Oh, and here's the thread on the users' mailing list regarding this bug.

http://groups-beta.google.com/group/cherrypy-users/browse_thread/thread/80eb4dc85e2a87f9

12/01/06 23:06:02: Modified by fumanchu

  • description changed.
  • milestone set to 3.0.

12/01/06 23:43:45: Modified by fumanchu

  • status changed from new to closed.
  • resolution set to fixed.
Ok, maybe I just don't understand the role of a
default controller. The way I understand cherrypy's
routing methods (which may be wrong, I am very new
at this) is that classes represent directories,
and functions represent files. So since the default
handler is a function, it should be considered a
file under a directory, and be able to redirect
to other files in that directory.

That's a bit of a non sequitur, since URI's are not files or directories, and HTTP is not a file system. All handlers are exposed functions, and return responses "based on the request content". See http://roy.gbiv.com/pubs/dissertation/evaluation.htm#sec_6_2_3

The upshot is that any handler, and therefore any URI, should be able to respond with a redirect. This includes "normal" handlers, as well as "default" and "index" handlers.

For instance, if I redirect from the index handler
I would get a file in that directory. So if I have
an index handler for /students/, which redirects to
'contact', I would get /students/contact, but not for
a default handler. This behavior seems inconsistent
to me, but that could be because of a lack of under-
standing on my part.

Correct; it is inconsistent. A test case which demonstrates the problem is in [1461], and a fix in [1463]. Note that this differs from the proposed patch; not all default methods should be flagged as index handlers--only those which are looked up using a URI with a trailing slash. Also, for the reasons stated above, the name will remain "is_index", not "is_dir" for now. This is less than ideal, but the full semantic would dictate an attribute named "request.rightmost_path_segment_is_empty_except_possible_parameters" and that's not fun to type nor remember. ;) See http://rfc.net/rfc2396.html#s3.3.

Hosted by WebFaction

Log in as guest/cpguest to create tickets