# HG changeset patch # User k0s # Date 1252350948 14400 # Node ID af82aaec0377496d4f18831c4f0c4a4dc18eeabb initial import of redirector diff -r 000000000000 -r af82aaec0377 ini-redirector.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ini-redirector.ini Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,10 @@ +[/foo.txt] +to = /bar.txt +type = 301 +created = August 1, 2009 + +[http://127.0.0.1:5521/(.*)] +to = http://localhost:5521/\1 +type = metarefresh +seconds = 10 +created = August 1, 2009 diff -r 000000000000 -r af82aaec0377 redirector.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector.ini Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,24 @@ +#!/usr/bin/env paster + +[DEFAULT] +debug = true +email_to = jhammel@openplans.org +smtp_server = localhost +error_email_from = paste@localhost + +[server:main] +use = egg:Paste#http +host = 0.0.0.0 +port = 5521 + +[composite:main] +use = egg:Paste#urlmap +/ = redirector + +set debug = false + +[app:redirector] +paste.app_factory = redirector.factory:factory +redirector.redirector = ini-redirector +redirector.ini-redirector.ini = %(here)s/ini-redirector.ini +app.directory = %(here)s/sample \ No newline at end of file diff -r 000000000000 -r af82aaec0377 redirector/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/__init__.py Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,1 @@ +# diff -r 000000000000 -r af82aaec0377 redirector/factory.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/factory.py Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,18 @@ +import os +from redirector import Redirector +from paste.httpexceptions import HTTPExceptionHandler +from paste.urlparser import StaticURLParser + +def factory(global_conf, **app_conf): + """create a sample redirector""" + assert 'app.directory' in app_conf + directory = app_conf['app.directory'] + assert os.path.isdir(directory) + keystr = 'redirector.' + args = dict([(key.split(keystr, 1)[-1], value) + for key, value in app_conf.items() + if key.startswith(keystr) ]) + app = StaticURLParser(directory) + redirector = Redirector(app, **args) + return HTTPExceptionHandler(redirector) + diff -r 000000000000 -r af82aaec0377 redirector/redirector.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/redirector.py Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,137 @@ +""" +redirector: a view with webob +""" + +import re + +from genshi.template import TemplateLoader +from pkg_resources import iter_entry_points +from pkg_resources import resource_filename +from urlparse import urlparse +from webob import Request, Response, exc + +class Redirector(object): + + ### class level variables + defaults = { 'auto_reload': 'False', + 'auth': 'False', + 'date_format': '%c', + 'path': '.redirect', + 'redirector': None, + 'seconds': 5, # seconds for a meta-refresh tag to redirect + } + + def __init__(self, app, **kw): + self.app = app # WSGI app + + # set instance attributes + for key in self.defaults: + setattr(self, key, kw.get(key, self.defaults[key])) + self.response_functions = { 'GET': self.get, + 'POST': self.post, + } + # bool options + for var in 'auto_reload', 'auth': + setattr(self, var, getattr(self, var).lower() == 'true') + + # pick a redirector back end + assert self.redirector + if isinstance(self.redirector, basestring): + name = self.redirector + self.redirector = iter_entry_points('redirector.redirectors', name=name).next() + # take first entry point; + # will raise StopIteration if there are none + + self.redirector = self.redirector.load() + keystr = name + '.' + kwargs = dict([(key.split(keystr, 1)[1], value) + for key, value in kw.items() + if key.startswith(keystr)]) + self.redirector = self.redirector(**kwargs) + + # genshi template loader + templates_dir = resource_filename(__name__, 'templates') + self.loader = TemplateLoader(templates_dir, + auto_reload=self.auto_reload) + + # redirect exceptions + self.status_map = dict([(key, value) + for key, value in exc.status_map.items() + if key in set([301, 302, 307])]) + + + ### methods dealing with HTTP + def __call__(self, environ, start_response): + request = Request(environ) + + path = request.path_info.strip('/').split('/') + if path and path[0] == self.path: + ### TODO: check if authorized + res = self.make_response(request) + return res(environ, start_response) + else: + + ### query redirection tables + for redirect in self.redirector.redirects(): + + _from = redirect['from'] + parsed_url = urlparse(_from) + if parsed_url[0]: + url = request.url + else: + url = request.path_info + from_re = re.compile(_from) + + # redirect on match + match = from_re.match(url) + if match: + location = from_re.sub(redirect['to'], url) + _type = redirect['type'] + if isinstance(_type, int): + raise self.status_map[_type](location=location) + else: + res = self.meta_refresh(request, redirect, location) + return res(environ, start_response) + + return self.app(environ, start_response) + + def make_response(self, request): + return self.response_functions.get(request.method, self.error)(request) + + def get_response(self, text, content_type='text/html'): + res = Response(content_type=content_type, body=text) + return res + + def get(self, request): + """ + return response to a GET requst + """ + + data = { 'redirects': self.redirector.redirects(), + 'status_map': self.status_map, + 'date_format': self.date_format } + + template = self.loader.load('redirects.html') + res = template.generate(**data).render('html', doctype='html') + return self.get_response(res) + + def post(self, request): + """ + return response to a POST request + """ + return self.get(request) + + def error(self, request): + """deal with non-supported methods""" + return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys()) + + + def meta_refresh(self, request, redirect, location): + data = { 'request': request, + 'redirect': redirect, + 'location': location, + 'seconds': redirect.get('seconds', self.seconds), + 'date_format': self.date_format } + template = self.loader.load('metarefresh.html') + res = template.generate(**data).render('html', doctype='html') + return self.get_response(res) diff -r 000000000000 -r af82aaec0377 redirector/redirectors.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/redirectors.py Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,71 @@ +from datetime import datetime +from dateutil.parser import parse +from ConfigParser import ConfigParser + + +class TestRedirector(object): + def redirects(self): + return [ { 'from': '/foo.txt', 'to': '/bar.txt', + 'type': 301, + 'created': datetime.now(), + 'expires': None }, + { 'from': 'http://127.0.0.1:5521/(.*)', + 'to': r'http://localhost:5521/\1', + 'type': 'metarefresh', + 'seconds': 10, + 'created': datetime.now(), + 'expires': None }, + ] + def set(self, _from, to, type=301, expires=None, seconds=None, reason=None): + # test only....does not set anything + return + + def add(self, _from, to, type=301, expires=None, seconds=None): + self.set(_from, to, type, expires, seconds) + +class IniRedirector(object): + def __init__(self, ini): + self.ini = ini + + def redirects(self): + parser = ConfigParser() + parser.read(self.ini) + + redirects = [] + + for section in parser.sections(): + redirect = { 'from': section } + assert parser.has_option(section, 'to') + assert parser.has_option(section, 'type') + redirect['to'] = parser.get(section, 'to') + if parser.has_option(section, 'created'): + redirect['created'] = parse(parser.get(section, 'created')) + else: + redirect['created'] = None + _type = parser.get(section, 'type') + try: + _type = int(_type) + except ValueError: + assert _type == 'metarefresh' + redirect['type'] = _type + if parser.has_option(section, 'expires'): + redirect['expires'] = parse(parser.get(section, 'expires')) + else: + redirect['expires'] = None + if parser.has_option(section, 'reason'): + redirect['reason'] = parser.get(section, 'reason') + if parser.has_option(section, 'seconds'): + redirect['seconds'] = parser.getint(section, 'seconds') + redirects.append(redirect) + return redirects + + def set(self, _from, to, type=301, expires=None, seconds=None, reason=None): + parser = ConfigParser() + parser.read(self.ini) + raise NotImplementedError # TODO + + def add(self, _from, to, type=301, expires=None, seconds=None): + raise NotImplementedError # TODO + parser = ConfigParser() + parser.read(self.ini) + self.set(_from, to, type, expires, seconds) diff -r 000000000000 -r af82aaec0377 redirector/templates/metarefresh.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/templates/metarefresh.html Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,25 @@ + + + + + + + +
+ You are being from ${request.url} to ${location} in ${seconds} seconds. + Please update any bookmarks to this page. +
+ +
+ This redirect will expire ${redirect['expires'].strftime(date_format)} +
+ +
+ ${redirect['reason']} +
+ + + diff -r 000000000000 -r af82aaec0377 redirector/templates/redirects.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/redirector/templates/redirects.html Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,77 @@ + + + + redirects + + +

redirects

+ + + + + + + + + + + + + + + + + + + + + + +
FromToTypeCreatedExpires
${redirect['from']}${redirect['to']} + + ${str(redirect['type'])} ${status_map[redirect['type']].title} + + meta refresh tag + ${redirect['created'].strftime(date_format)} + + + +
+ +

Add a redirect

+ +
+
+
From
+
+ +
To
+
+ +
Type
+
+ +
+ +
Expires
+
+ +
+ +
Reason for this redirect
+
+ +
+
+ + +
+ + + diff -r 000000000000 -r af82aaec0377 sample/bar.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sample/bar.txt Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,1 @@ +bar diff -r 000000000000 -r af82aaec0377 sample/foo.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sample/foo.txt Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,1 @@ +foo diff -r 000000000000 -r af82aaec0377 sample/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sample/index.html Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,9 @@ + + +foo bar + + +

foo

+

bar

+ + diff -r 000000000000 -r af82aaec0377 setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.py Mon Sep 07 15:15:48 2009 -0400 @@ -0,0 +1,38 @@ +from setuptools import setup, find_packages +import sys, os + +version = "0.1" + +setup(name='redirector', + version=version, + description="WSGI middleware/app for managing redirects", + long_description=""" +""", + classifiers=[], # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers + author='Jeff Hammel', + author_email='jhammel@openplans.org', + url='http://k0s.org', + license="", + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + include_package_data=True, + zip_safe=False, + install_requires=[ + # -*- Extra requirements: -*- + 'WebOb', + 'Paste', + 'PasteScript', + 'genshi', + 'python-dateutil', + + ], + entry_points=""" + # -*- Entry points: -*- + [paste.app_factory] + main = redirector.factory:factory + + [redirector.redirectors] + test-redirector = redirector.redirectors:TestRedirector + ini-redirector = redirector.redirectors:IniRedirector + """, + ) +