Skip to content

Commit

Permalink
Merge pull request #457 from gratipay/base_url
Browse files Browse the repository at this point in the history
add base URL canonicalization to algorithm
  • Loading branch information
pjz committed Jun 29, 2015
2 parents 03bed23 + 2ad1868 commit 3bb3068
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 9 deletions.
4 changes: 4 additions & 0 deletions aspen/algorithms/website.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ def raise_200_for_OPTIONS(request):
raise Response(200)


def redirect_to_base_url(website, request):
website.canonicalize_base_url(request)


def dispatch_request_to_filesystem(website, request):

if website.list_directories:
Expand Down
8 changes: 6 additions & 2 deletions aspen/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import aspen.logging
from aspen.configuration import parse
from aspen.exceptions import ConfigurationError
from aspen.utils import ascii_dammit
from aspen.utils import ascii_dammit, BaseURLCanonicalizer
from aspen.typecasting import defaults as default_typecasters
import aspen.body_parsers

Expand All @@ -30,7 +30,8 @@

# 'name': (default, from_unicode)
KNOBS = \
{ 'changes_reload': (False, parse.yes_no)
{ 'base_url': (None, parse.identity)
, 'changes_reload': (False, parse.yes_no)
, 'charset_dynamic': ('UTF-8', parse.charset)
, 'charset_static': (None, parse.charset)
, 'indices': (default_indices, parse.list_)
Expand Down Expand Up @@ -220,6 +221,9 @@ def safe_getcwd(errorstr):
self.media_type_json: aspen.body_parsers.jsondata
}

# install a base URL canonicalizer
self.canonicalize_base_url = BaseURLCanonicalizer(self.base_url)

# load renderers
self.renderer_factories = {}
for name in aspen.BUILTIN_RENDERERS:
Expand Down
12 changes: 6 additions & 6 deletions aspen/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ def nice(t):
raise TypeError(msg)


# Hostname canonicalization
# Base URL Canonicalization
# =========================

def _extract_scheme(request):
Expand All @@ -395,16 +395,16 @@ def _extract_scheme(request):
def _extract_host(request):
return request.headers['Host'] # will 400 if missing

def Canonizer(expected, permanent_redirect=False, extract_scheme=_extract_scheme,
def BaseURLCanonicalizer(expected, permanent_redirect=False, extract_scheme=_extract_scheme,
extract_host=_extract_host):
"""Takes a netloc such as http://localhost:8080 (no path part).
"""Takes a base_url such as http://localhost:8080 (no path part).
"""

def noop(request):
pass

def canonize(request):
"""Enforce a certain network location.
def func(request):
"""Enforce a certain base URL.
"""

scheme = extract_scheme(request)
Expand All @@ -424,7 +424,7 @@ def canonize(request):
uri += '/'
request.redirect(uri, permanent=permanent_redirect)

return expected and canonize or noop
return expected and func or noop


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions doc/api/website/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ arguments to Website:</p>

<table>
<tr><td><b><u>attribute</u></b></td><td><b><u>default</u></b></td> </tr>
<tr><td>base_url</td><td>''</td> </tr>
<tr><td>changes_reload</td><td>False</td> </tr>
<tr><td>charset_dynamic</td><td>UTF-8</td> </tr>
<tr><td>charset_static</td><td>None</td> </tr>
Expand Down
3 changes: 2 additions & 1 deletion doc/configuration/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ like <code>ASPEN_NAME</code> and a keyword argument to `aspen.website.Website`.
Here is an example configuration:</p>

<table>
<tr><td><b><u>name</u></b></td> <td><b><u>default</u></b></td> </tr>
<tr><td><b><u>name</u></b></td> <td><b><u>example value</u></b></td> </tr>
<tr><td>base_url</td> <td>http://www.example.com</td> </tr>
<tr><td>changes_reload</td> <td>yes</td> </tr>
<tr><td>charset_dynamic</td> <td>ISO-8859-1</td> </tr>
<tr><td>charset_static</td> <td>windows-1252</td> </tr>
Expand Down
45 changes: 45 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,48 @@ def test_to_rfc822():
expected = 'Thu, 01 Jan 1970 00:00:00 GMT'
actual = to_rfc822(datetime(1970, 1, 1))
assert actual == expected


def test_base_url_canonicalizer_canonicalizes_base_url(harness):
harness.fs.www.mk(('index.html', 'Greetings, program!'))
harness.client.hydrate_website(base_url='http://example.com')
response = harness.client.GxT()
assert response.code == 302
assert response.headers['Location'] == 'http://example.com/'

def test_base_url_canonicalizer_includes_path_and_qs_for_GET(harness):
harness.fs.www.mk(('index.html', 'Greetings, program!'))
harness.client.hydrate_website(base_url='http://example.com')
response = harness.client.GxT('/foo/bar?baz=buz')
assert response.code == 302
assert response.headers['Location'] == 'http://example.com/foo/bar?baz=buz'

def test_base_url_canonicalizer_redirects_to_homepage_for_POST(harness):
harness.fs.www.mk(('index.html', 'Greetings, program!'))
harness.client.hydrate_website(base_url='http://example.com')
response = harness.client.PxST('/foo/bar?baz=buz')
assert response.code == 302
assert response.headers['Location'] == 'http://example.com/'

def test_base_url_canonicalizer_allows_good_base_url(harness):
harness.fs.www.mk(('index.html', 'Greetings, program!'))
harness.client.hydrate_website(base_url='http://localhost')
response = harness.client.GET()
assert response.code == 200
assert response.body == 'Greetings, program!'

def test_base_url_canonicalizer_is_noop_without_base_url(harness):
harness.fs.www.mk(('index.html', 'Greetings, program!'))
harness.client.hydrate_website()
response = harness.client.GET()
assert response.code == 200
assert response.body == 'Greetings, program!'

def test_base_url_canonicalizer_is_really_noop_without_base_url(harness):
harness.client.hydrate_website()
harness.client.website.algorithm['redirect_to_base_url'](harness.client.website, None)

def test_base_url_canonicalizer_is_not_noop_with_base_url(harness):
harness.client.hydrate_website(base_url='foo')
with raises(AttributeError):
harness.client.website.algorithm['redirect_to_base_url'](harness.client.website, None)

0 comments on commit 3bb3068

Please sign in to comment.