web.redirect breaking Apache URL rewriting

92 views
Skip to first unread message

Alex Quinn

unread,
Jan 29, 2012, 1:33:40 PM1/29/12
to web.py
PROBLEM
It seems that web.redirect (and web.seeother, etc.) break the URL
scheme when used in conjunction with an Apache URL rewriting. I set
up a slight modification of the Todo example (http://webpy.org/src/
todo-list/0.3) and used the Apache redirect to hide the ".../todo.py/"
from the URLs. When it hits the web.redirect("/"), instead of
redirecting to http://example.com/todo/, it redirects to
http://example.com/todo/todo.py/. Besides making my clean URLs dirty
again, that also breaks relative URLs to static content.

SOLUTION
I'd like to offer a very small patch that solves the problem. The
core issue seems to be that web.redirect (webapi.py) adds web.ctx.home
to the front of the given URL, and web.home is constructed using the
SCRIPT_NAME environment variable instead of REQUEST_URI. SCRIPT_NAME
is apparently not affected by the Appache rewrite. For SCRIPT_NAME, I
get "/todo_test/todo.py". For REQUEST_URI, I get "/todo_test/" (or
something like that).

QUESTIONS
Would a patch be welcome?
Should I open a ticket in the issue tracker?
Would a stripped-down example of the issue help clarify this?

Huge Kudos to Aaron and others for making Web.py so easy to use *and*
so easy to modify! The code was remarkably clear and easy to navigate.

Alex Quinn

unread,
Jan 29, 2012, 3:26:19 PM1/29/12
to web.py
Below is a stripped down example that illustrates the problem. If I
go to http://example.com/webpy_bug_report/ it works fine. If I go to
http://example.com/webpy_bug_report/redirect/ I expect to be
redirected to http://example.com/webpy_bug_report/ but instead I am
redirected to http://example.com/webpy_bug_report/redirect_problem.py/.
I've tried this under plain CGI as well as FastCGI.

Upon further testing, I realized my current fix doesn't quite work.
If this is indeed a bug, I'd be happy to take another try at fixing
it. However, it seems like this would have come up for others
before. I couldn't find anything in the list archives or the issue
tracker. If I'm doing something wrong or if I missed a solution
online somewhere, I'd be most grateful if somebody could point me in
the right direction.

Thanks,
Alex


___________________________________________________________
# .htaccess

RewriteEngine on
RewriteBase /webpy_bug_report/
RewriteCond %{REQUEST_URI} !^/webpy_bug_report/favicon.ico$
RewriteCond %{REQUEST_URI} !^(/.*)+redirect_problem.py/
RewriteRule ^(.*)$ redirect_problem.py/$1 [PT]


___________________________________________________________
# redirect_problem.py

import web

urls = (
'/', 'index',
'/redirect/','redirect'
)

class index:
def GET(self):
return "This is the index page."

class redirect:
def GET(self):
raise web.seeother('/')

app = web.application(urls, globals())

if __name__ == '__main__':
app.run()

Dragan Espenschied

unread,
Jan 30, 2012, 4:28:39 AM1/30/12
to we...@googlegroups.com
This redirecting problem is indeed annoying, I usually "solve" it by changing an
environment variable, for example like this:

os.environ['REAL_SCRIPT_NAME'] = '/webpy_bug_report/'

This environment is the source for the prefixes of webpy's redirects.

I would love if there would be a solution that works across all web servers and
systems, I do not have enough experience with different setups though.

Maybe the easiest addition to webpy would be a web.config setting like
"base_url" that would be used in preference over the environment variable.

Bests,
Dragan

--
http://noobz.cc/
http://digitalfolklore.org/
http://contemporary-home-computing.org/1tb/

Alex Quinn

unread,
Jan 30, 2012, 2:49:20 PM1/30/12
to web.py
Thanks, Dragan.

That's a novel solution. I hadn't thought of changing an environment
variable, but it sounds like it would work. As for a
web.config.base_url, in theory the web.homepath is supposed to do
exactly that. As far as I can tell, it's just broken (or at least
doesn't work how I expected).

For now, I'm using the function below. Instead of calling
web.seeother(url), I call web.seeother(make_url(url)).

If this is an acknowledged issue and a patch would be welcome, I could
probably take another shot at a more general fix that would hopefully
not have any unintended consequences.

I had also been grappling with how to handle links embedded in the
templates, but I just thought of a solution: Put a <BASE href="http://
example.com/myapp/"> tag with the application's base URL in the <HEAD>
of the base template.

Alex





___________________________________________________________________________
def make_url(url):
"""
Fix an apparent incompatibility between how I'm using Apache URL
rewriting
and the web.seeother (and web.redirect, etc.) commands.

- If input is full URL (i.e., http://example.com/blah/), return as
is.
- If input starts with "/", treat as relative to this
application's base.
- Otherwise, append it to the current URL, minus any ending
filename.
"""
import urlparse, os

if "://" not in url:
url_parts =
list(urlparse.urlparse(web.ctx.env["REQUEST_URI"]))
old_path = url_parts[2]

if url.startswith("/"):
base = os.path.basename(__file__)
home = web.ctx.home
if home.endswith(base) and not
old_path.endswith(__file__):
home = home[:0-len(base)]
url = home.rstrip("/") + url
else:
new_path = os.path.normpath(os.path.join(old_path, url))
url_parts[2] = new_path
url = urlparse.urlunparse(url_parts)
return url
___________________________________________________________________________







On Jan 30, 4:28 am, Dragan Espenschied <d...@a-blast.org> wrote:
> This redirecting problem is indeed annoying, I usually "solve" it by changing an
> environment variable, for example like this:
>
> os.environ['REAL_SCRIPT_NAME'] = '/webpy_bug_report/'
>
> This environment is the source for the prefixes of webpy's redirects.
>
> I would love if there would be a solution that works across all web servers and
> systems, I do not have enough experience with different setups though.
>
> Maybe the easiest addition to webpy would be a web.config setting like
> "base_url" that would be used in preference over the environment variable.
>
> Bests,
> Dragan
>
> Am 29.01.2012 21:26, schrieb Alex Quinn:
>
>
>
>
>
>
>
>
>
> > Below is a stripped down example that illustrates the problem.  If I
> > go tohttp://example.com/webpy_bug_report/it works fine.  If I go to
> >http://example.com/webpy_bug_report/redirect/I expect to be
> > redirected tohttp://example.com/webpy_bug_report/but instead I am
> > redirected tohttp://example.com/webpy_bug_report/redirect_problem.py/.

Dragan Espenschied

unread,
Jan 31, 2012, 7:51:00 AM1/31/12
to we...@googlegroups.com
On http://webpy.org/cookbook/ctx we written under "Data Found in ctx":

> homepath � The part of the path requested by the user which was trimmed off
> the current app. That is homepath + path = the path actually requested in
> HTTP by the user. E.g. /admin This seems to be derived during startup from
> the environment variable REAL_SCRIPT_NAME. It affects what web.url() will
> prepend to supplied urls. This in turn affects where web.seeother() will go,
> which might interact badly with your url rewriting scheme (e.g. mod_rewrite)

"This seems to be derived from environment variable REAL_SCRIPT_NAME."

Seems? :)

All redirecting http resonses are handled via the Redirect class in webapi.py:

class Redirect(HTTPError):
"""A `301 Moved Permanently` redirect."""
def __init__(self, url, status='301 Moved Permanently', absolute=False):
"""
Returns a `status` redirect to the new URL.
`url` is joined with the base URL so that things like
`redirect("about") will work properly.
"""
newloc = urlparse.urljoin(ctx.path, url)

if newloc.startswith('/'):
if absolute:
home = ctx.realhome
else:
home = ctx.home
newloc = home + newloc

headers = {
'Content-Type': 'text/html',
'Location': newloc
}
HTTPError.__init__(self, status, headers, "")


Here we have the variable 'realhome' which is assigned in application.py:

ctx.homedomain = ctx.protocol + '://' + env.get('HTTP_HOST', '[unknown]')
ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))
ctx.home = ctx.homedomain + ctx.homepath
#@@ home is changed when the request is handled to a sub-application.
#@@ but the real home is required for doing absolute redirects.
ctx.realhome = ctx.home

So probably, changin ctx.realhome would help. I have no experience with
sub-applications though.

It would be great if anybody knowledgeable could chime in and recommend the best
technique for setting the redirect base.

Bests,
Dragan

Alex Quinn

unread,
Feb 4, 2012, 4:42:28 PM2/4/12
to web.py
Thanks, Dragan. That's exactly the code I tried to patch, initially--
and exactly the reason why my patch was no good. A fix should not
break sub-applications or anything else.

Alex




On Jan 31, 7:51 am, Dragan Espenschied <d...@a-blast.org> wrote:
> Onhttp://webpy.org/cookbook/ctxwe written under "Data Found in ctx":
>
> > homepath The part of the path requested by the user which was trimmed off
> > - If input is full URL (i.e.,http://example.com/blah/), return as is. - If
> > input starts with "/", treat as relative to this application's base. -
> > Otherwise, append it to the current URL, minus any ending filename. """
> > import urlparse, os
>
> > if "://" not in url: url_parts =
> > list(urlparse.urlparse(web.ctx.env["REQUEST_URI"])) old_path = url_parts[2]
>
> > if url.startswith("/"): base = os.path.basename(__file__) home =
> > web.ctx.home if home.endswith(base) and not old_path.endswith(__file__): home
> > = home[:0-len(base)] url = home.rstrip("/") + url else: new_path =
> > os.path.normpath(os.path.join(old_path, url)) url_parts[2] = new_path url =
> > urlparse.urlunparse(url_parts) return url
> > ___________________________________________________________________________
>
> > On Jan 30, 4:28 am, Dragan Espenschied <d...@a-blast.org> wrote:
> >> This redirecting problem is indeed annoying, I usually "solve" it by
> >> changing an environment variable, for example like this:
>
> >> os.environ['REAL_SCRIPT_NAME'] = '/webpy_bug_report/'
>
> >> This environment is the source for the prefixes of webpy's redirects.
>
> >> I would love if there would be a solution that works across all web servers
> >> and systems, I do not have enough experience with different setups though.
>
> >> Maybe the easiest addition to webpy would be a web.config setting like
> >> "base_url" that would be used in preference over the environment variable.
>
> >> Bests, Dragan
>
> >> Am 29.01.2012 21:26, schrieb Alex Quinn:
>
> >>> Below is a stripped down example that illustrates the problem.  If I go
> >>> tohttp://example.com/webpy_bug_report/itworks fine.  If I go to
> >>> tohttp://example.com/webpy_bug_report/butinstead I am redirected
> >>> tohttp://example.com/webpy_bug_report/redirect_problem.py/. I've tried
> >>> this under plain CGI as well as FastCGI.
>
> >>> Upon further testing, I realized my current fix doesn't quite work. If
> >>> this is indeed a bug, I'd be happy to take another try at fixing it.
> >>> However, it seems like this would have come up for others before.  I
> >>> couldn't find anything in the list archives or the issue tracker.  If I'm
> >>> doing something wrong or if I missed a solution online somewhere, I'd be
> >>> most grateful if somebody could point me in the right direction.
>
> >>> Thanks, Alex
>
> >>> ___________________________________________________________ # .htaccess
>
> >>> RewriteEngine on RewriteBase /webpy_bug_report/ RewriteCond
> >>> %{REQUEST_URI} !^/webpy_bug_report/favicon.ico$ RewriteCond
> >>> %{REQUEST_URI} !^(/.*)+redirect_problem.py/ RewriteRule ^(.*)$
> >>> redirect_problem.py/$1 [PT]
>
> >>> ___________________________________________________________ #
> >>> redirect_problem.py
>
> >>> import web
>
> >>> urls = ( '/',         'index', '/redirect/','redirect' )
>
> >>> class index: def GET(self): return "This is the index page."
>
> >>> class redirect: def GET(self): raise web.seeother('/')
>
> >>> app = web.application(urls, globals())
>
> >>> if __name__ == '__main__': app.run()
>
> >> --http://noobz.cc/http://digitalfolklore.org/http://contemporary-home-c...
>
> --http://noobz.cc/http://digitalfolklore.org/http://contemporary-home-computing.org/1tb/
Reply all
Reply to author
Forward
0 new messages