views:

808

answers:

4

How can I prevent a user from accessing my app at example.appspot.com and force them to access it at example.com? I already have example.com working, but I don't want users to be able to access the appspot domain. I'm using python.

+1  A: 

i have no idea how mysql game does it. it looks like a redirect at appspot.

http://mysqlgame.appspot.com/

edit: i just looked at the headers, and its returning a

301 Moved Permanently

So it must be possible to configure appspot to return this

mkoryak
They have moved to a new server, according to their home page. Their app must automatically do a self.response.redirect() for all requests.
Josh Patton
+8  A: 

You can check if os.environ['HTTP_HOST'].endswith('.appspot.com') -- if so, then you're serving from something.appspot.com and can send a redirect, or otherwise alter your behavior as desired.

You could deploy this check-and-redirect-if-needed (or other behavior alteration of your choice) in any of various ways (decorators, WSGI middleware, inheritance from an intermediate base class of yours that subclasses webapp.RequestHandler [[or whatever other base handler class you're currently using]] and method names different than get and post in your application-level handler classes, and others yet) but I think that the key idea here is that os.environ is set by the app engine framework according to CGI standards and so you can rely on those standards (similarly WSGI builds its own environment based on the values it picks up from os.environ).

Alex Martelli
Thanks, exactly what I was looking for.
Josh Patton
+3  A: 
def redirect_from_appspot(wsgi_app):
def redirect_if_needed(env, start_response):
    if env["HTTP_HOST"].startswith('my_app_name.appspot.com'):
        import webob, urlparse
        request = webob.Request(env)
        scheme, netloc, path, query, fragment = urlparse.urlsplit(request.url)
        url = urlparse.urlunsplit([scheme, 'www.my_domain.com', path, query, fragment])
        start_response('301 Moved Permanently', [('Location', url)])
        return ["301 Moved Peramanently",
              "Click Here" % url]
    else:
        return wsgi_app(env, start_response)
return redirect_if_needed
LarsOn
How would you integrate this with an application?
Josh Patton
Got this working, but I don't think it's possible to make it work with static content, so that will still be accessible via appspot (and all the various version'd subdomains) :-(
Danny Tuppeny
This code doesn't handle SSL or cron jobs. I've posted a slightly modified version in a new answer.
Danny Tuppeny
+2  A: 

The code posted above has two problems - it tries to redirect secure traffic (which isn't supported on custom domains), and also your cron jobs will fail when Google call them on your appspot domain and you serve up a 301.

I posted a slightly modified version to my blog: http://blog.dantup.com/2009/12/redirecting-requests-from-appid-appspot-com-to-a-custom-domain

I've included the code below for convenience.

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

def run_app(url_mapping):
    application = webapp.WSGIApplication(url_mapping, debug=True)
    application = redirect_from_appspot(application)
    run_wsgi_app(application)

def redirect_from_appspot(wsgi_app):
    """Handle redirect to my domain if called from appspot (and not SSL)"""
    from_server = "dantup-blog.appspot.com"
    to_server = "blog.dantup.com"

    def redirect_if_needed(env, start_response):

        # If we're calling on the appspot address, and we're not SSL (SSL only works on appspot)
        if env["HTTP_HOST"].endswith(from_server) and env["HTTPS"] == "off":

            # Parse the URL
            import webob, urlparse
            request = webob.Request(env)
            scheme, netloc, path, query, fragment = urlparse.urlsplit(request.url)
            url = urlparse.urlunsplit([scheme, to_server, path, query, fragment])

            # Exclude /admin calls, since they're used by Cron, TaskQueues and will fail if they return a redirect
            if not path.startswith('/admin'):
                # Send redirect
                start_response("301 Moved Permanently", [("Location", url)])
                return ["301 Moved Peramanently", "Click Here %s" % url]

        # Else, we return normally
        return wsgi_app(env, start_response)

    return redirect_if_needed
Danny Tuppeny
Thanks, deployed your code (+1). I wonder why Google doesn't offer built-in solution for this, since they usually encourages good practices such as url canonicalization.
jholster
I wondered the same thing. It's actually quite annoying having to put this code into all my apps (and change the domains) :-(
Danny Tuppeny