Commits

Ian Lewis committed 7d07901

Initial commit

Comments (0)

Files changed (39)

+syntax: glob
+*.orig
+*.rej
+*~
+*.bak
+*.marks
+*.o
+*.pyc
+*.swp
+*.egg-info
+build
+dist
+docs/ja/build
+pip-log.txt
+
+syntax: regexp
+.*\#.*\#$ 
+Ian Lewis <ian@beproud.jp>
+Copyright (c) 2010, BeProud Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer
+
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the authors nor the names of its contributors
+   may be used to endorse or promote products derived from this
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS.
+include README.rst AUTHORS LICENSE requirements.txt tests.py
+recursive-include docs *
+================
+bpssl
+================
+
+bpssl is a Django application that helps you support
+HTTPS on your website. The main functionality is performing redirection
+for HTTPS only URLs and views. For instance, if a request for your
+login view '/login' is recieved over HTTP, the provided middleware can
+redirect the user to the equivalent HTTPS page.
+
+Specifying views and urls as secure is supported as are `flatpages`_. `Fastcgi`_
+and HTTP proxy setups are also well supported. See the documentation at:
+
+http://beproud.bitbucket.org/bpssl-1.0/
+
+bpssl draws inspiration from the well known SSL Middleware snippets on
+djangosnippets.org. It roughly supports the features of the following
+snippets:
+
+* http://djangosnippets.org/snippets/880/
+* http://djangosnippets.org/snippets/240/
+* http://djangosnippets.org/snippets/1999/
+
+Middleware
+---------------------
+
+bpssl provides an ``SSLRedirectMiddleware`` which can redirect users from
+secure pages to non-secure pages and visa-versa. Urls are set up by adding
+regular expressions to the ``SSL_URLS`` setting in settings.py.
+``SSLRedirectMiddleware`` can also be extended to support more specific use
+cases.
+
+ssl_view decorator
+---------------------
+
+bpssl provides an ``ssl_view()`` decorator which can be used instead of the
+``SSL_URLS`` to specify that a particular view should be secure.
+
+.. _flatpages: http://docs.djangoproject.com/en/dev/ref/contrib/flatpages/
+.. _Fastcgi: http://docs.djangoproject.com/en/dev/howto/deployment/fastcgi

beproud/__init__.py

+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = locals()['__path__'] # make PyFlakes happy
+    __path__ = extend_path(__path__, __name__)

beproud/django/__init__.py

+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = locals()['__path__'] # make PyFlakes happy
+    __path__ = extend_path(__path__, __name__)
Add a comment to this file

beproud/django/ssl/__init__.py

Empty file added.

beproud/django/ssl/conf.py

+#:coding=utf-8:
+
+from django.conf import settings as django_settings
+
+settings = type('Settings', (object,), {})()
+
+settings.USE_SSL = getattr(django_settings, "USE_SSL", True)
+settings.SSL_URLS = getattr(django_settings, "SSL_URLS", ())
+settings.SSL_IGNORE_URLS = getattr(django_settings, "SSL_IGNORE_URLS", ())
+settings.SSL_REQUEST_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')

beproud/django/ssl/context_processors.py

+#:coding=utf-8:
+
+from django.http import get_host
+from django.conf import settings as django_settings
+from beproud.django.ssl.conf import settings
+
+__all__ = ('conf',)
+
+def conf(request):
+    return {
+        'USE_SSL': settings.USE_SSL,
+        'HTTP_HOST': getattr(django_settings, 'HTTP_HOST', get_host(request)),
+        'SSL_HOST': getattr(django_settings, 'SSL_HOST', get_host(request)),
+    }

beproud/django/ssl/decorators.py

+#:coding=utf-8:
+
+from django.utils.decorators import decorator_from_middleware
+
+from beproud.django.ssl.middleware import SSLMiddleware
+
+__all__ = ('ssl_view',)
+
+def ssl_view(view):
+    """
+    Requires that a view be accessed via SSL(HTTPS). Calls
+    to this view where request.is_secure() returns False
+    will redirect to the SSL(HTTPS) version of the view.
+    """
+    wrapped_view = decorator_from_middleware(SSLMiddleware)(view)
+    # Exempt this view from processing by middleware.
+    wrapped_view.ssl_exempt = True
+    return wrapped_view

beproud/django/ssl/middleware.py

+# vim:fileencoding=utf8
+import re
+
+from django.http import HttpResponseRedirect, Http404, get_host
+from django.conf import settings as django_settings
+from django.core import urlresolvers
+
+from beproud.django.ssl.conf import settings
+
+__all__ = (
+    'SSLMiddleware',
+    'SSLRedirectMiddleware',
+    'SSLProxyMiddleware',
+)
+
+class SSLMiddleware(object):
+    """
+    Redirects all http requests to the equivalent https url
+    """
+    def process_request(self, request):
+        """
+        Processes the request and redirects to the appropriate
+        url if necessary. 
+        
+        Processing is done by process_request rather than
+        process_view because if a url is not matched
+        then process_view is not called and some badly implemented
+        middleware such as FlatpageFallbackMiddleware could return
+        responses which would end up bypassing the SSLMiddleware
+        processing.
+        """
+        # Resolve the view from the path_info. urlresolvers info
+        # is cached so this isn't too expensive.
+        callback = None
+        callback_args = None
+        callback_kwargs = None
+        try:
+            callback, callback_args, callback_kwargs = urlresolvers.resolve(request.path_info)
+
+            # If the view exists but we aren't handling it then return immediately
+            # Whether the URL is handled or not doesn't matter
+            if not self.is_handle_view(request, callback, callback_args, callback_kwargs):
+                return
+        except Http404:
+            pass
+
+        # Only handle the request if either the view doesn't exist and handle_url==True
+        # or the view exists and is_handle_view==True and handle_url==True
+        handle_url = self.is_handle_url(request)
+        if handle_url:
+            secure = self.is_secure_url(request)
+            # If the request is https but ssl is not required redirect to http
+            # If the request is http but ssl is required redirect to https
+            if not secure == self.is_secure_request(request):
+                return self._redirect(request, secure)
+
+    def is_handle_url(self, request):
+        """
+        Return true if this URL should be handled. The request
+        may still be handled if the view uses the ssl_view decorator.
+        """
+        return settings.USE_SSL
+
+    def is_handle_view(self, request, callback, callback_args, callback_kwargs):
+        """
+        Returns whether the view should be handled. The request
+        may still be handled if the view uses the ssl_view decorator.
+        """
+        return settings.USE_SSL
+
+    def is_secure_url(self, request):
+        """
+        Returns True when the requested URL should only be accessed
+        by secure requests.
+        """
+        return True
+
+    def is_secure_request(self, request):
+        """
+        Returns True when the request is a secure request.
+        """
+        return request.is_secure()
+
+    def _redirect(self, request, secure):
+        """
+        Redirect to the proper http or https URL.
+        """
+        # TODO: Use django site framework instead of HTTP_HOST/SSL_HOST?
+        protocol = secure and "https" or "http"
+        if secure:
+            host = getattr(django_settings, 'SSL_HOST', get_host(request))
+        else:
+            host = getattr(django_settings, 'HTTP_HOST', get_host(request))
+        newurl = "%s://%s%s" % (protocol,host,request.get_full_path())
+        if django_settings.DEBUG and request.method == 'POST':
+            raise RuntimeError, \
+        """Django can't perform a SSL redirect while maintaining POST data.
+           Please structure your views so that redirects only occur during GETs."""
+
+        return HttpResponseRedirect(newurl)
+
+class SSLRedirectMiddleware(SSLMiddleware):
+    """
+    Redirects all http requests directed at urls matching regular expressions
+    set in the SSL_URLS in settings.py to the equivalent https url.
+    """
+
+    def __init__(self, urls=None, ignore_urls=None):
+        if not urls:
+            urls = settings.SSL_URLS
+        self.urls = map(lambda u: re.compile(u) if isinstance(u, basestring) else u, urls)
+        if not ignore_urls:
+            ignore_urls = settings.SSL_IGNORE_URLS
+        self.ignore_urls = map(lambda u: re.compile(u) if isinstance(u, basestring) else u, ignore_urls)
+
+    def is_handle_url(self, request):
+        """
+        Only handle urls that are not in SSL_IGNORE_URLS
+        """
+        for url in self.ignore_urls:
+            if url.match(request.path_info):
+                return False
+        return True
+
+    def is_handle_view(self, request, callback, callback_args, callback_kwargs):
+        """
+        Don't handle views that are marked as ssl_exempt
+        """
+        return settings.USE_SSL and not getattr(callback, 'ssl_exempt', False)
+
+    def is_secure_url(self, request):
+        """
+        Return True if the requested url matches a URL
+        in SSL_URLS
+        """
+        secure = False
+        for url in self.urls:
+            if url.match(request.path_info):
+                secure = True
+                break
+        return secure
+
+class SSLProxyMiddleware(object):
+    """
+    Patches the request object so that is_secure() returns True
+    when using an HTTP Proxy and the HTTP header defined by the
+    SSL_REQUEST_HEADER setting is set to 'on' or 'https'. This
+    way a header can be set in the style of HTTPS=on or PROTOCOL=https
+
+    Typical settings for the SSL_REQUEST_HEADER setting would be
+    HTTP_X_FORWARDED_SSL or HTTP_X_FORWARDED_PROTOCOL.
+
+    This middleware should be set as early as possible in your
+    MIDDLEWARE_CLASSES setting.
+    """
+    def __init__(self, header_name=None, header_value=None):
+        default_header_name, default_header_value = settings.SSL_REQUEST_HEADER
+        if not header_name:
+            header_name = default_header_name
+        if not header_value:
+            header_value = default_header_value
+        self.header_name = header_name.upper()
+        self.header_value = header_value
+
+    def process_request(self, request):
+        if self.header_name:
+            if request.META.get(self.header_name,'').lower() == self.header_value.lower():
+                request.is_secure = lambda: True
+
Add a comment to this file

beproud/django/ssl/models.py

Empty file added.

beproud/django/ssl/tests/__init__.py

+from wsgi_tests import *
+from proxy_tests import *

beproud/django/ssl/tests/base.py

+#:coding=utf-8:
+
+from urlparse import urlsplit, urlunsplit
+
+from django.core import exceptions
+from django.utils.importlib import import_module
+from django.test import TestCase as DjangoTestCase
+from django.http import HttpRequest, HttpResponse, QueryDict
+from django.conf import settings as django_settings
+
+from beproud.django.ssl.conf import settings
+
+AVAILABLE_SETTINGS = ['SSL_REQUEST_HEADER',]
+
+class BaseTestCase(object):
+    SSL_REQUEST_HEADER = None
+    MIDDLEWARE_CLASSES = (
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+    )
+
+    def setUp(self):
+        self._old_MIDDLEWARE_CLASSES = django_settings.MIDDLEWARE_CLASSES
+        if self.MIDDLEWARE_CLASSES is not None:
+            django_settings.MIDDLEWARE_CLASSES = self.MIDDLEWARE_CLASSES
+
+        for setting_name in AVAILABLE_SETTINGS:
+            setting_value = getattr(self, setting_name, None)
+            if setting_value:
+                setattr(self, "_old_"+setting_name, getattr(settings, setting_name, None))
+                setattr(settings, setting_name, setting_value)
+
+    def tearDown(self):
+        if self.MIDDLEWARE_CLASSES != self._old_MIDDLEWARE_CLASSES:
+            django_settings.MIDDLEWARE_CLASSES = self._old_MIDDLEWARE_CLASSES
+ 
+        for setting_name in AVAILABLE_SETTINGS:
+            old_setting_value = getattr(self, "_old_"+setting_name, None)
+            if old_setting_value is None:
+                if hasattr(settings, setting_name):
+                    delattr(settings, setting_name)
+            else:
+                setattr(settings, setting_name, old_setting_value)
+
+    def assertRedirects(self, response, expected_url, status_code=302,
+                        target_status_code=200, host=None, msg_prefix=''):
+        """Asserts that a response redirected to a specific URL, and that the
+        redirect URL can be loaded.
+
+        Note that assertRedirects won't work for external links since it uses
+        TestClient to do a request.
+        """
+        if msg_prefix:
+            msg_prefix += ": "
+
+        if hasattr(response, 'redirect_chain'):
+            # The request was a followed redirect
+            self.failUnless(len(response.redirect_chain) > 0,
+                msg_prefix + "Response didn't redirect as expected: Response"
+                " code was %d (expected %d)" %
+                    (response.status_code, status_code))
+
+            self.assertEqual(response.redirect_chain[0][1], status_code,
+                msg_prefix + "Initial response didn't redirect as expected:"
+                " Response code was %d (expected %d)" %
+                    (response.redirect_chain[0][1], status_code))
+
+            url, status_code = response.redirect_chain[-1]
+
+            self.assertEqual(response.status_code, target_status_code,
+                msg_prefix + "Response didn't redirect as expected: Final"
+                " Response code was %d (expected %d)" %
+                    (response.status_code, target_status_code))
+
+        else:
+            # Not a followed redirect
+            self.assertEqual(response.status_code, status_code,
+                msg_prefix + "Response didn't redirect as expected: Response"
+                " code was %d (expected %d)" %
+                    (response.status_code, status_code))
+
+            url = response['Location']
+            scheme, netloc, path, query, fragment = urlsplit(url)
+
+            redirect_response = self.get(urlunsplit((scheme, netloc, path, None, None)), QueryDict(query))
+
+            # Get the redirection page, using the same client that was used
+            # to obtain the original response.
+            self.assertEqual(redirect_response.status_code, target_status_code,
+                msg_prefix + "Couldn't retrieve redirection page '%s':"
+                " response code was %d (expected %d)" %
+                    (path, redirect_response.status_code, target_status_code))
+
+        e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
+        if not (e_scheme or e_netloc):
+            expected_url = urlunsplit(('http', host or 'testserver', e_path,
+                e_query, e_fragment))
+
+        self.assertEqual(url, expected_url,
+            msg_prefix + "Response redirected to '%s', expected '%s'" %
+                (url, expected_url))
+
+    def request(self, path, method='GET', https=False, headers={}):
+        if https or urlsplit(path)[0] == 'https':
+            headers = headers.copy()
+            headers.update({'SERVER_PORT': '443'})
+        return getattr(self.client, method.lower())(path, **headers)
+    def get(self, path, https=False, headers={}):
+        return self.request(path, https=https, headers=headers)
+    def post(self, path, https=False, headers={}):
+        return self.request(path, method='POST', https=https, headers=headers)
+
+class SSLRedirectTests(object):
+
+    def test_http_no_redirect(self):
+        response = self.get("/some/other/url")
+        self.assertContains(response, "Spam and Eggs")
+
+    def test_http_redirect(self):
+        response = self.get("/sslurl/someurl")
+        self.assertRedirects(response, "https://testserver/sslurl/someurl")
+
+    def test_https_no_redirect(self):
+        response = self.get("/sslurl/someurl", https=True)
+        self.assertContains(response, "Spam and Eggs")
+
+    def test_https_redirect(self):
+        response = self.get("/some/other/url", https=True)
+        self.assertRedirects(response, "http://testserver/some/other/url")
+
+class SSLDecoratorTests(object):
+
+    def test_http_redirect(self):
+        response = self.get("/decorated/ssl/view")
+        self.assertRedirects(response, "https://testserver/decorated/ssl/view")
+
+    def test_https_redirect(self):
+        response = self.get("/decorated/ssl/view", https=True)
+        self.assertContains(response, "Spam and Eggs")
+
+class UseSSLTests(object):
+    def setUp(self):
+        self._old_USE_SSL = settings.USE_SSL
+        settings.USE_SSL = False
+
+    def tearDown(self):
+        settings.USE_SSL = self._old_USE_SSL 
+
+    def test_use_ssl_decorator(self):
+        response = self.get("/decorated/ssl/view")
+        self.assertContains(response, "Spam and Eggs")
+
+        response = self.get("/decorated/ssl/view", https=True)
+        self.assertContains(response, "Spam and Eggs")
+         
+    def test_use_ssl_middleware(self):
+        response = self.get("/sslurl/someurl")
+        self.assertContains(response, "Spam and Eggs")
+
+    def test_https_redirect(self):
+        response = self.get("/sslurl/someurl", https=True)
+        self.assertContains(response, "Spam and Eggs")
+
+class FlatpageTests(object):
+    def setUp(self):
+        super(FlatpageTests,self).setUp()
+        import os
+        from django.contrib.flatpages.models import FlatPage
+        from django.contrib.sites.models import Site
+
+        self._old_TEMPLATE_DIRS = django_settings.TEMPLATE_DIRS
+        django_settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), 'templates'),)
+        django_settings.MIDDLEWARE_CLASSES += (
+            'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
+        )
+        fp = FlatPage.objects.create(
+            url='/flatpage/',
+            title='Non-secure Flatpage',
+            content='Non-secure Flatpage',
+            enable_comments=False,
+            template_name='flatpage.html',
+            registration_required=False,
+        )
+        fp.sites.add(Site.objects.get_current())
+        fp = FlatPage.objects.create(
+            url='/sslurl/flatpage/',
+            title='Secure Flatpage',
+            content='Secure Flatpage',
+            enable_comments=False,
+            template_name='flatpage.html',
+            registration_required=False,
+        )
+        fp.sites.add(Site.objects.get_current())
+
+    def tearDown(self):
+        super(FlatpageTests,self).tearDown()
+        django_settings.TEMPLATE_DIRS = self._old_TEMPLATE_DIRS
+
+    def test_http_flatpage_http(self):
+        self.assertContains(self.get('/flatpage/'), 'Non-secure Flatpage')
+
+    def test_http_flatpage_https(self):
+        self.assertRedirects(
+            self.get('/flatpage/', https=True),
+            'http://testserver/flatpage/',
+        )
+
+    def test_https_flatpage_http(self):
+        self.assertRedirects(
+            self.get('/sslurl/flatpage/'),
+            'https://testserver/sslurl/flatpage/',
+        )
+
+    def test_https_flatpage_https(self):
+        self.assertContains(    
+            self.get('/sslurl/flatpage/', https=True),
+            'Secure Flatpage',
+        )

beproud/django/ssl/tests/proxy_tests.py

+#:coding=utf-8:
+
+from urlparse import urlsplit, urlunsplit
+
+from django.test import TestCase as DjangoTestCase
+
+from beproud.django.ssl.tests.base import (
+    BaseTestCase, SSLRedirectTests, SSLDecoratorTests,
+    UseSSLTests, FlatpageTests
+)
+
+class ProxyTestCase(BaseTestCase):
+    SSL_REQUEST_HEADER = ('HTTP_X_FORWARDED_SSL', 'on')
+    MIDDLEWARE_CLASSES = (
+        'beproud.django.ssl.middleware.SSLProxyMiddleware',
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+    )
+
+    def request(self, path, method='GET', https=False, headers={}):
+        if https or urlsplit(path)[0] == 'https':
+            headers = headers.copy()
+            headers.update({'HTTP_X_FORWARDED_SSL': 'ON'})
+        return super(ProxyTestCase, self).request(path, method, https, headers)
+
+class ProxySSLRedirectTestCase(SSLRedirectTests, ProxyTestCase, DjangoTestCase):
+    pass
+class ProxySSLDecoratorTestCase(SSLDecoratorTests, ProxyTestCase, DjangoTestCase):
+    pass
+class ProxyUseSSLTestCase(UseSSLTests, ProxyTestCase, DjangoTestCase):
+    pass
+class ProxyFlatpageTestCase(FlatpageTests, ProxyTestCase, DjangoTestCase):
+    pass
+
+class ProxyProtocolTestCase(BaseTestCase):
+    SSL_REQUEST_HEADER = ('HTTP_X_PROXY_REQUEST_PROTOCOL', 'https')
+    MIDDLEWARE_CLASSES = (
+        'beproud.django.ssl.middleware.SSLProxyMiddleware',
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+    )
+
+    def request(self, path, method='GET', https=False, headers={}):
+        if https or urlsplit(path)[0] == 'https':
+            headers = headers.copy()
+            headers.update({'HTTP_X_PROXY_REQUEST_PROTOCOL': 'HTTPS'})
+        return super(ProxyProtocolTestCase, self).request(path, method, https, headers)
+
+class ProxyProtocolSSLRedirectTestCase(SSLRedirectTests, ProxyProtocolTestCase, DjangoTestCase):
+    pass
+class ProxyProtocolSSLDecoratorTestCase(SSLDecoratorTests, ProxyProtocolTestCase, DjangoTestCase):
+    pass
+class ProxyProtocolUseSSLTestCase(UseSSLTests, ProxyProtocolTestCase, DjangoTestCase):
+    pass
+class ProxyProtocolFlatpageTestCase(FlatpageTests, ProxyProtocolTestCase, DjangoTestCase):
+    pass

beproud/django/ssl/tests/templates/404.html

+<html><body>404</body></html>

beproud/django/ssl/tests/templates/flatpage.html

+<html><body>{{ flatpage.content }}</body></html>

beproud/django/ssl/tests/urls.py

+#:coding=utf-8:
+from django.conf.urls.defaults import *
+from django.http import HttpResponse
+from django.views.decorators.cache import never_cache
+
+from beproud.django.ssl.decorators import ssl_view
+
+urlpatterns = patterns('',
+    (r'sslurl/someurl', lambda request: HttpResponse("Spam and Eggs")),
+    (r'some/other/url', lambda request: HttpResponse("Spam and Eggs")),
+    (r'decorated/ssl/view', ssl_view(lambda request: HttpResponse("Spam and Eggs"))),
+    # Test decorating multiple times
+    (r'decorated/ssl/view', never_cache(ssl_view(lambda request: HttpResponse("Spam and Eggs")))),
+)

beproud/django/ssl/tests/wsgi_tests.py

+#:coding=utf-8:
+
+import os
+from urlparse import urlsplit, urlunsplit
+
+from django.test import TestCase as DjangoTestCase
+
+from beproud.django.ssl.tests.base import (
+    BaseTestCase, SSLRedirectTests, SSLDecoratorTests,
+    UseSSLTests, FlatpageTests,
+)
+
+class WSGITestCase(BaseTestCase):
+    def request(self, path, method='GET', https=False, headers={}):
+        if https or urlsplit(path)[0] == 'https':
+            headers = headers.copy()
+            headers.update({'wsgi.url_scheme': 'https'})
+        return super(WSGITestCase, self).request(path, method, https, headers)
+
+class WSGISSLRedirectTestCase(SSLRedirectTests, WSGITestCase, DjangoTestCase):
+    pass
+class WSGISSLDecoratorTestCase(SSLDecoratorTests, WSGITestCase, DjangoTestCase):
+    pass
+class WSGIUseSSLTestCase(UseSSLTests, WSGITestCase, DjangoTestCase):
+    pass
+class WSGIFlatpageTestCase(FlatpageTests, WSGITestCase, DjangoTestCase):
+    pass
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf build/*
+
+html:
+	mkdir -p build/html build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
+	@echo
+	@echo "Build finished. The HTML pages are in build/html."
+
+pickle:
+	mkdir -p build/pickle build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+	mkdir -p build/json build/doctrees
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	mkdir -p build/htmlhelp build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in build/htmlhelp."
+
+latex:
+	mkdir -p build/latex build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p build/changes build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
+	@echo
+	@echo "The overview file is in build/changes."
+
+linkcheck:
+	mkdir -p build/linkcheck build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in build/linkcheck/output.txt."
+
+pdf:
+	$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) build/pdf
+	@echo
+	@echo "Build finished. The PDF files are in build/pdf."

docs/en/source/conf.py

+# -*- coding: utf-8 -*-
+#
+# Kay Documentation documentation build configuration file, created by
+# sphinx-quickstart on Mon Aug 24 10:58:49 2009.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath('../../../'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+# extensions = ['sphinx.ext.autodoc', 'rst2pdf.pdfbuilder']
+#extensions = ['sphinx.ext.autodoc', 'rst2pdf.pdfbuilder']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+html_theme = 'haiku'
+html_theme_options = {
+    'textcolor': '#456',
+    'linkcolor': '#A00000',
+    'visitedlinkcolor': '#A00000',
+}
+html_logo='beproud.png'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'bpssl'
+copyright = u'2010, BeProud Inc.'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+language = 'en'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+#html_style = 'default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+#html_favicon = 'favicon.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'bpssl'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'bpssl.tex', ur'bpssl Documentation', ur'Ian Lewis', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+# -- Options for PDF output --------------------------------------------------
+
+# Grouping the document tree into PDF files. List of tuples
+# (source start file, target name, title, author, options).
+#
+# If there is more than one author, separate them with \\.
+# For example: r'Guido van Rossum\\Fred L. Drake, Jr., editor'
+#
+# The options element is a dictionary that lets you override
+# this config per-document.
+# For example,
+# ('index', u'MyProject', u'My Project', u'Author Name',
+# dict(pdf_compressed = True))
+# would mean that specific document would be compressed
+# regardless of the global pdf_compressed setting.
+
+pdf_documents = [
+  ('index', u'bpssl-Documentation', u'bpssl Documentation', u'Ian Lewis'),
+]
+
+# A comma-separated list of custom stylesheets. Example:
+# pdf_stylesheets = ['sphinx','kerning','a4']
+pdf_stylesheets = ['sphinx','kerning','a4','ja']
+
+# Create a compressed PDF
+# Use True/False or 1/0
+# Example: compressed=True
+#pdf_compressed = False
+
+# A colon-separated list of folders to search for fonts. Example:
+# pdf_font_path = ['/usr/share/fonts', '/usr/share/texmf-dist/fonts/']
+pdf_font_path = ['/usr/share/fonts', '/usr/share/texmf-dist/fonts/']
+
+# Language to be used for hyphenation support
+#pdf_language = "en_US"
+
+# Mode for literal blocks wider than the frame. Can be
+# overflow, shrink or truncate
+#pdf_fit_mode = "shrink"
+
+# Section level that forces a break page.
+# For example: 1 means top-level sections start in a new page
+# 0 means disabled
+#pdf_break_level = 0
+
+# When a section starts in a new page, force it to be 'even', 'odd',
+# or just use 'any'
+#pdf_breakside = 'any'
+
+# Insert footnotes where they are defined instead of
+# at the end.
+#pdf_inline_footnotes = True
+
+# verbosity level. 0 1 or 2
+#pdf_verbosity = 0
+
+# If false, no index is generated.
+#pdf_use_index = True
+
+# If false, no modindex is generated.
+#pdf_use_modindex = True
+
+# If false, no coverpage is generated.
+#pdf_use_coverpage = True
+
+# Documents to append as an appendix to all manuals.
+#pdf_appendices = []
+
+# Enable experimental feature to split table cells. Use it
+# if you get "DelayedTable too big" errors
+#pdf_splittables = False
+
+# Set the default DPI for images
+#pdf_default_dpi = 72

docs/en/source/index.rst

+bpssl Documentation
+===================================
+
+What is bpssl?
+-----------------------------------
+
+bpssl is a Django application that helps you support
+HTTPS on your website. The main functionality is performing redirection
+for HTTPS only URLs and views. For instance, if a request for your
+login view '/login' is recieved over HTTP, the provided middleware can
+redirect the user to the equivalent HTTPS page.
+
+Specifying views and urls as secure is supported as are `flatpages`_. `Fastcgi`_
+and HTTP proxy setups are also well supported. See the sourcecode/homepage at:
+
+http://bitbucket.org/beproud/bpssl/
+
+bpssl draws inspiration from the well known SSL Middleware snippets on
+http://www.djangosnippets.org . It roughly supports the features of the following
+snippets:
+
+* http://djangosnippets.org/snippets/880/
+* http://djangosnippets.org/snippets/240/
+* http://djangosnippets.org/snippets/1999/
+
+Contents:
+
+.. toctree::
+  :numbered:
+  :maxdepth: 2
+
+  install
+  usage
+  settings
+
+Indices and tables
+===================================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+.. _`flatpages`: http://docs.djangoproject.com/en/dev/ref/contrib/flatpages/
+.. _Fastcgi: http://docs.djangoproject.com/en/dev/howto/deployment/fastcgi

docs/en/source/install.rst

+===================================
+Installation
+===================================
+
+Application Install
+-----------------------------
+
+Installing bpssl is easy, but there are some non-obvious
+steps for setting up your web server that you will need to do to get
+it working.
+
+First install the ``bpssl`` package using PIP::
+
+    $ pip install bpssl
+
+or easy_install::
+
+    $ easy_install bpssl
+
+Next add ``'beproud.django.ssl'`` to your `INSTALLED_APPS`_ in your ``settings.py``.
+
+.. code-block:: python 
+
+    INSTALLED_APPS = (
+        'django.contrib.auth',
+        'django.contrib.contenttypes',
+        'django.contrib.sessions',
+        'django.contrib.sites',
+        'django.contrib.messages',
+        # ...
+        'beproud.django.ssl',
+        # ...
+    )
+
+Context Processor Setup
+-----------------------------
+
+.. function:: beproud.django.ssl.context_processors.ssl
+
+Add the :func:`ssl <beproud.django.ssl.context_processors.ssl>` context processor to
+your `TEMPLATE_CONTEXT_PROCESSORS`_ setting.
+
+.. code-block:: python
+
+    TEMPLATE_CONTEXT_PROCESSORS = (
+        #...
+        'beproud.django.ssl.context_processors.ssl',
+        #...
+    )
+
+Middleware Setup
+-----------------------------
+
+Add ``'beproud.django.ssl.middleware.SSLRedirectMiddleware'`` to your `MIDDLEWARE_CLASSES`_ setting. 
+
+.. code-block:: python 
+
+    MIDDLEWARE_CLASSES = (
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.middleware.csrf.CsrfViewMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+        'django.contrib.messages.middleware.MessageMiddleware',
+        # ...
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+        # ...
+    )
+
+If using a HTTP proxy you will need to add
+``'beproud.django.ssl.middleware.SSLProxyMiddleware'`` to `MIDDLEWARE_CLASSES`_.
+``SSLProxyMiddleware`` should be added as early as possible in your `MIDDLEWARE_CLASSES`_
+setting.
+
+.. code-block:: python 
+
+    MIDDLEWARE_CLASSES = (
+        'beproud.django.ssl.middleware.SSLProxyMiddleware',
+        # ...
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.middleware.csrf.CsrfViewMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+        'django.contrib.messages.middleware.MessageMiddleware',
+        # ...
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+        # ...
+    )
+
+.. _install-web-server-setup:
+
+Web Server Setup
+----------------------------
+
+Because SSL decoding and encoding is done by the web server, it's impossible for a
+Django application to know whether a request is secure or not unless the web server
+tells it. In order to pass that information to the Django application some setup on
+the webserver is usually necessary. In particular because ``beproud.django.ssl``
+relies on the `request.is_secure()`_ method we need to get `request.is_secure()`_
+to return the right results.
+
+nginx/FastCGI
++++++++++++++++++++++++++++++
+
+When using nginx and FastCGI it's sufficient to set the information in a
+``fastcgi_param``. Setting the fastcgi parameter ``HTTPS`` to ``on`` will tell the
+flup server that the request is a secure request. Flup will then wrap the request
+accordingly, setting the ``wsgi.url_scheme`` to ``https`` and making
+`request.is_secure()`_ return the correct value.
+
+.. code-block:: nginx 
+
+    location / {
+        include                 /etc/nginx/fastcgi_params;
+        fastcgi_pass            upstream;
+        fastcgi_param           HTTPS on;
+    }
+
+nginx/HTTP proxy
++++++++++++++++++++++++++++++
+
+When using nginx as a HTTP reverse proxy you will need to pass information about
+whether a request is secure or not in an HTTP header. In order to avoid falling
+victim to man in the middle attacks where an attacker could cause data that
+should be sent over a secure channel to be sent over an unsecure
+channel, nginx will need to set or strip this header for non-secure requests.
+
+Set the name and value of the header to the value you set in the 
+:ref:`SSL_REQUEST_HEADER <setting-ssl-request-header>` setting in order to use it
+in conjunction with the
+:class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>`.
+
+.. code-block:: nginx
+
+    #HTTP
+    server {
+        listen 80;
+        location / {
+            proxy_pass          http://myproxy;    
+            
+            # We need to set this header for HTTP requests as well
+            # so that we won't fall victim to man-in-the-middle attacks.
+            proxy_set_header HTTP_X_FORWARDED_PROTOCOL      "http";
+            # ...
+        }
+    }
+     
+    # HTTPS
+    server {
+        listen 443;
+        ssl on;
+        # ...
+        location / {
+            proxy_pass          http://myproxy;    
+            # This should be set to the same headeras the
+            # non-ssl setup above.
+            proxy_set_header    HTTP_X_FORWARDED_PROTOCOL   https; 
+            # ...
+        }
+    }
+
+.. Apache/FastCGI
+.. +++++++++++++++++++++++++++++
+.. 
+.. TODO: Write later
+
+Apache/mod_wsgi
++++++++++++++++++++++++++++++
+
+In an Apache/mod_wsgi setup where HTTPS is handled by the same server, mod_wsgi will
+set the ``wsgi.url_scheme`` environment variable appropriately and
+`request.is_secure()`_ should return the correct value without any special setup.
+
+.. _`request.is_secure()`: http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.is_secure
+.. _`INSTALLED_APPS`: http://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
+.. _`MIDDLEWARE_CLASSES`: http://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
+.. _`TEMPLATE_CONTEXT_PROCESSORS`: http://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors

docs/en/source/settings.rst

+Settings
+================================================================
+
+.. module:: beproud.django.ssl.conf
+   :synopsis: Settings 
+.. moduleauthor:: Ian Lewis <ian@beproud.jp>
+.. currentmodule:: beproud.django.ssl.conf
+
+.. _setting-ssl-urls:
+
+SSL_URLS
+---------------------
+
+:ref:`SSL_URLS <setting-ssl-urls>` is a list of regular expressions that are matched against the request url
+by :class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+to determine if a request should be processed or redirected to a secure or non-secure
+url. See 
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+for an explanation of how :ref:`SSL_URLS <setting-ssl-urls>` are used.
+
+.. code-block:: python
+
+    SSL_URLS = (
+        '^/login/',
+        '^/purchase/'
+        ...
+    )
+
+.. _setting-ssl-ignore-urls:
+
+SSL_IGNORE_URLS
+---------------------
+
+``SSL_IGNORE_URLS`` are urls that can be accessed by secure as well as non-secure
+requests.
+
+.. code-block:: python
+
+    SSL_IGNORE_URLS = (
+        '^/static/',
+        ...
+    )
+
+.. _setting-ssl-host:
+
+SSL_HOST
+---------------------
+
+Sometimes websites serve secure pages via a seperate domain such as
+``secure.example.com`` to further express that the page that the user is visiting
+is secure.
+
+``SSL_HOST`` sets the domain of the ssl host. When redirecting to ssl urls 
+``beproud.django.ssl`` will redirect to this host. The default is to redirect
+to the same host as the non-secure request.
+
+.. code-block:: python
+
+    SSL_HOST = 'secure.example.com'
+
+.. _setting-http-host:
+
+HTTP_HOST
+---------------------
+
+``HTTP_HOST`` sets the domain of the non-secure host. When redirecting
+non-secure urls ``beproud.django.ssl`` will redirect to this host. The default
+is to redirect to the same host as the secure request.  So when ``SSL_HOST``
+is set you will almost always want to set this setting as well.
+
+.. code-block:: python
+
+    HTTP_HOST = 'example.com'
+
+.. _setting-ssl-request-header:
+
+SSL_REQUEST_HEADER
+---------------------
+
+When using the :class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>`, 
+``SSL_REQUEST_HEADER`` should be set to the name and value of the HTTP header, in the form of a two
+tuple, that is forwarded from the reverse proxy server for secure requests.
+
+Typical settings for the ``SSL_REQUEST_HEADER`` setting would be
+('HTTP_X_FORWARDED_SSL', 'on') or ('HTTP_X_FORWARDED_PROTOCOL', 'https')
+
+The default value for ``SSL_REQUEST_HEADER`` is shown below.
+
+.. code-block:: python
+
+    SSL_REQUEST_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
Add a comment to this file

docs/en/source/static/beproud.png

Added
New image

docs/en/source/usage.rst

+===================================
+Limiting access to secure requests
+===================================
+
+Limiting Access via URLs
+-----------------------------------
+
+One way to specify which requests should be treated as secure is to identify them
+by url. This way setting entire sets of urls to be secure is easy and maintenance
+is fairly painless.
+
+For instance one might want to set all of a user's account information to be
+accessed via secure requests so you could set all urls under ``/accounts/`` to
+be secure urls. The same might apply for making payments under ``/payment`` or
+some other such scheme.
+
+Using the SSLRedirectMiddleware
++++++++++++++++++++++++++++++++++++
+
+.. class:: beproud.django.ssl.middleware.SSLRedirectMiddleware
+
+Using the
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+you can add secure request redirection over your whole site.
+
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+uses a setting called :ref:`SSL_URLS <setting-ssl-urls>` to determine which urls will
+be accessed via SSL (HTTPS). You set this setting the in your
+``settings.py``.  :ref:`SSL_URLS <setting-ssl-urls>` is a list of regular expressions
+that are checked against the request url as it is found in `request.path_info`_.
+If one of the regular expressions matches then the URL is treated as a secure url 
+and non-secure accesses are redirected to the secure url. For instance, using the
+setup below if a request is recieved for the url ``http://www.example.com/login/``,
+the user will be redirected to ``https://www.example.com/login/`` because the url
+matches a the :ref:`SSL_URLS <setting-ssl-urls>` settings. The paths matched against
+the regular expression are different from ``urls.py`` in that they start with a '/'
+so keep that in mind.
+
+.. code-block:: python
+
+    SSL_URLS = (
+        '^/login/',
+        '^/purchase/'
+        # ...
+    )
+
+In case there are some urls you want to be accessable via HTTP or HTTPS, you
+can set those in the :ref:`SSL_IGNORE_URLS <setting-ssl-ignore-urls>` setting. For
+instance, static files served by your application or i18n javascript would typically
+be accessable via both HTTP and HTTPS. :ref:`SSL_IGNORE_URLS <setting-ssl-ignore-urls>`
+is defined exactly like :ref:`SSL_URLS <setting-ssl-urls>`.
+
+.. code-block:: python
+
+    SSL_IGNORE_URLS = (
+        '^/i18n_js$',
+        '^/static/',
+        # ...
+    )
+
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>` does the following:
+
+* If the request url matches a url in :ref:`SSL_IGNORE_URLS <setting-ssl-ignore-urls>` then do nothing.
+* If the request url matches a url in :ref:`SSL_URLS <setting-ssl-urls>` and the request is not-secure then the user is redirected to the secure version of the page. 
+* If the request url does not match a url in :ref:`SSL_URLS <setting-ssl-urls>` and the request is secure then the user is redirected to the non-secure version of the page. 
+
+.. note:: 
+
+    Secure requests to non-secure pages are redirected to the non-secure url because
+    pages that can be accessed via multiple urls can confuse search engines and is
+    not particularly `RESTful`_.
+
+Limiting Access to Views
+-----------------------------------
+
+One might also want to limit access to particular views because they encapsulate
+some kind of functionality that should be accessed in a secure way. Multiple
+urls could be accessing this view so it may not make sense to maintain the
+security settings as URLs.
+
+The RAW Way
++++++++++++++++++++++++++++++++++++
+
+Because some urls may change or multiple urls could be handled by the same view,
+you may want to limit access to secure requests at the view level. The simple raw
+way to limit access to pages to secure requests is to check `request.is_secure()`_
+to see if the request is secure::
+
+    from django.http import HttpResponseRedirect
+    from django.http import get_host
+
+    def my_secure_view(request):
+        if request.is_secure():
+            return HttpResponseRedirect("https://%s%s" % (get_host(request), request.get_full_path()))
+        # ...
+
+However, by limiting access this way the :class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>` has no way of
+knowing that the page is secure and thus may conflict with your view unless you
+add the URL to :ref:`SSL_IGNORE_URLS <setting-ssl-ignore-urls>`.
+
+The ssl_view decorator
++++++++++++++++++++++++++++++++++++
+
+.. function:: beproud.django.ssl.decorators.ssl_view 
+
+As a shortcut the ``ssl_view()`` decorator is provided to indicate that your view
+should be restricted to secure requests. Example::
+
+    from beproud.django.ssl.decorators import ssl_view 
+
+    @ssl_view
+    def my_secure_view(request):
+        ...
+
+``ssl_view()`` implements the same functionality as the
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+but works without having to set the URL in :ref:`SSL_URLS <setting-ssl-urls>`.
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+will also recognize views using the ``ssl_view()`` decorator and won't conflict with
+your view.
+
+Using an HTTP Reverse Proxy
+-----------------------------------
+
+.. class:: beproud.django.ssl.middleware.SSLProxyMiddleware 
+
+:class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>`
+will update the request object to make sure that
+`request.is_secure()`_ returns true when processing a secure request from a properly
+set up HTTP reverse proxy server. See
+:ref:`Web Server Setup <install-web-server-setup>` for more info on how to set up
+reverse proxy web servers to work with
+:class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>`.
+
+You will need to set the :ref:`SSL_REQUEST_HEADER <setting-ssl-request-header>`
+setting to the name and value of the header you use to pass whether a request
+is secure or not. The default value of this setting is shown below:
+
+.. code-block:: python
+
+    SSL_REQUEST_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')
+
+:class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>` does the following:
+
+* If the name and value of the HTTP header defined by :ref:`SSL_REQUEST_HEADER <setting-ssl-request-header>` equals the value 
+  of the header sent with the request then set `request.is_secure()`_ to return True.
+
+.. note::
+
+    :class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>` should be set as early as possible in your `MIDDLEWARE_CLASSES`_ setting so that
+    any middleware which looks at `request.is_secure()`_ will get the right value. At the very least it should be before your
+    :class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`.
+
+.. _`request.path_info`: http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.path_info
+.. _`request.is_secure()`: http://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.is_secure
+.. _`RESTful`: http://en.wikipedia.org/wiki/Representational_State_Transfer
+.. _`MIDDLEWARE_CLASSES`: http://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf build/*
+
+html:
+	mkdir -p build/html build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
+	@echo
+	@echo "Build finished. The HTML pages are in build/html."
+
+pickle:
+	mkdir -p build/pickle build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+	mkdir -p build/json build/doctrees
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	mkdir -p build/htmlhelp build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in build/htmlhelp."
+
+latex:
+	mkdir -p build/latex build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p build/changes build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
+	@echo
+	@echo "The overview file is in build/changes."
+
+linkcheck:
+	mkdir -p build/linkcheck build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in build/linkcheck/output.txt."
+
+pdf:
+	$(SPHINXBUILD) -b pdf $(ALLSPHINXOPTS) build/pdf
+	@echo
+	@echo "Build finished. The PDF files are in build/pdf."
+{
+  "embeddedFonts" :
+[["VL-Gothic-Regular.ttf","VL-PGothic-Regular.ttf","ipam.otf","verdanaz.ttf"]],
+  "fontsAlias" : {
+    "stdFont": "VL-PGothic-Regular",
+    "stdBold": "VL-PGothic-Regular",
+    "stdItalic": "VL-PGothic-Regular",
+    "stdBoldItalic": "VL-PGothic-Regular",
+    "stdMono": "VL-Gothic-Regular"
+  },
+  "styles" : [
+    ["base" , {
+      "wordWrap": "CJK"
+    }],
+    ["literal" , {
+      "wordWrap": "None"
+    }]
+  ]
+}
+

docs/ja/source/conf.py

+# -*- coding: utf-8 -*-
+#
+# Kay Documentation documentation build configuration file, created by
+# sphinx-quickstart on Mon Aug 24 10:58:49 2009.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath('../../../'))
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+# extensions = ['sphinx.ext.autodoc', 'rst2pdf.pdfbuilder']
+#extensions = ['sphinx.ext.autodoc', 'rst2pdf.pdfbuilder']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+html_theme = 'haiku'
+html_theme_options = {
+    'textcolor': '#456',
+    'linkcolor': '#A00000',
+    'visitedlinkcolor': '#A00000',
+}
+html_logo='beproud.png'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'bpssl'
+copyright = u'2010, 株式会社ビープラウド'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+language = 'ja'
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+#html_style = 'default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+#html_favicon = 'favicon.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'bpssl'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'bpssl.tex', ur'bpssl Documentation',
+   ur'ルイス・イアン', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+# -- Options for PDF output --------------------------------------------------
+
+# Grouping the document tree into PDF files. List of tuples
+# (source start file, target name, title, author, options).
+#
+# If there is more than one author, separate them with \\.
+# For example: r'Guido van Rossum\\Fred L. Drake, Jr., editor'
+#
+# The options element is a dictionary that lets you override
+# this config per-document.
+# For example,
+# ('index', u'MyProject', u'My Project', u'Author Name',
+# dict(pdf_compressed = True))
+# would mean that specific document would be compressed
+# regardless of the global pdf_compressed setting.
+
+pdf_documents = [
+  ('index', u'bpssl-Documentation', u'bpssl ドキュメント', u'ルイス・イアン'),
+]
+
+# A comma-separated list of custom stylesheets. Example:
+# pdf_stylesheets = ['sphinx','kerning','a4']
+pdf_stylesheets = ['sphinx','kerning','a4','ja']
+
+# Create a compressed PDF
+# Use True/False or 1/0
+# Example: compressed=True
+#pdf_compressed = False
+
+# A colon-separated list of folders to search for fonts. Example:
+# pdf_font_path = ['/usr/share/fonts', '/usr/share/texmf-dist/fonts/']
+pdf_font_path = ['/usr/share/fonts', '/usr/share/texmf-dist/fonts/']
+
+# Language to be used for hyphenation support
+#pdf_language = "en_US"
+
+# Mode for literal blocks wider than the frame. Can be
+# overflow, shrink or truncate
+#pdf_fit_mode = "shrink"
+
+# Section level that forces a break page.
+# For example: 1 means top-level sections start in a new page
+# 0 means disabled
+#pdf_break_level = 0
+
+# When a section starts in a new page, force it to be 'even', 'odd',
+# or just use 'any'
+#pdf_breakside = 'any'
+
+# Insert footnotes where they are defined instead of
+# at the end.
+#pdf_inline_footnotes = True
+
+# verbosity level. 0 1 or 2
+#pdf_verbosity = 0
+
+# If false, no index is generated.
+#pdf_use_index = True
+
+# If false, no modindex is generated.
+#pdf_use_modindex = True
+
+# If false, no coverpage is generated.
+#pdf_use_coverpage = True
+
+# Documents to append as an appendix to all manuals.
+#pdf_appendices = []
+
+# Enable experimental feature to split table cells. Use it
+# if you get "DelayedTable too big" errors
+#pdf_splittables = False
+
+# Set the default DPI for images
+#pdf_default_dpi = 72

docs/ja/source/index.rst

+bpssl ドキュメント
+===================================
+
+bpssl とは?
+-----------------------------------
+
+bpsslはDjango用のSSL対応アプリです。アクセスする時に
+HTTPSが必須なURLを指定することがよくあります。例えば、ログイン画面を
+HTTPSでしかアクセスできないようにする。ただし、HTTPでアクセスした場合、
+HTTPSのほうのURLにリダイレクトしたいこともよくあります。 bpssl
+はその対応を簡単にできるようなアプリです。
+
+URLでも、ビューでもSSLリダイレクトに対応していますし、 `flatpages`_
+にも対応しています。 `Fastcgi`_ にも、HTTP リバースプロクシーサーバーにも
+対応しています。ソースを以下のURLでご覧ください。
+
+http://bitbucket.org/beproud/bpssl/
+
+bpssl は http://www.djangosnippets.org に投稿したSSLミドルウエアから、インスピレーションを得た。
+以下のスニペットの機能にほぼ対応しています。
+
+* http://djangosnippets.org/snippets/880/
+* http://djangosnippets.org/snippets/240/
+* http://djangosnippets.org/snippets/1999/
+
+目次:
+
+.. toctree::
+  :numbered:
+  :maxdepth: 2
+
+  install
+  usage 
+  settings
+
+Indices and tables
+===================================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+.. _flatpages: http://djangoproject.jp/doc/ja/1.0/ref/contrib/flatpages.html
+.. _fastcgi: http://djangoproject.jp/doc/ja/1.0/howto/deployment/fastcgi.html

docs/ja/source/install.rst

+===================================
+インストール
+===================================
+
+アプリケーションインストール
+-----------------------------
+
+bpssl をインストールするのが簡単ですが、ウェブサーバーと
+連携するところがありますので、注意しないといけません。
+
+まずは、ポッケージを PIP でインストールします::
+
+    $ pip install bpssl
+
+もしくは ``easy_install`` で::
+
+    $ easy_install bpssl
+
+次に、 ``'beproud.django.ssl'`` を ``settings.py`` の `INSTALLED_APPS`_ に追加してください。
+
+.. code-block:: python 
+
+    INSTALLED_APPS = (
+        'django.contrib.auth',
+        'django.contrib.contenttypes',
+        'django.contrib.sessions',
+        'django.contrib.sites',
+        'django.contrib.messages',
+        #...
+        'beproud.django.ssl',
+        #...
+    )
+
+.. TODO: Context processor
+
+ミドルウエアを設定
+-----------------------------
+
+それから、 ``'beproud.django.ssl.middleware.SSLRedirectMiddleware'`` を `MIDDLEWARE_CLASSES`_ に追加してください。
+
+.. code-block:: python 
+
+    MIDDLEWARE_CLASSES = (
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.middleware.csrf.CsrfViewMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+        'django.contrib.messages.middleware.MessageMiddleware',
+        #...
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+        #...
+    )
+
+HTTPのリバースプロクシーを使う場合は、 
+``'beproud.django.ssl.middleware.SSLProxyMiddleware'`` を `MIDDLEWARE_CLASSES`_ に
+追加する必要があります。できるだけ上に登録すること。
+
+.. code-block:: python 
+
+    MIDDLEWARE_CLASSES = (
+        'beproud.django.ssl.middleware.SSLProxyMiddleware',
+        # ...
+        'django.middleware.common.CommonMiddleware',
+        'django.contrib.sessions.middleware.SessionMiddleware',
+        'django.middleware.csrf.CsrfViewMiddleware',
+        'django.contrib.auth.middleware.AuthenticationMiddleware',
+        'django.contrib.messages.middleware.MessageMiddleware',
+        # ...
+        'beproud.django.ssl.middleware.SSLRedirectMiddleware',
+        # ...
+    )
+
+.. _install-web-server-setup:
+
+ウェブサーバーの設定
+----------------------------
+
+SSLのエンコードやデコードがウェブサーバーで行うため、Djangoのアプリケーション側で、
+リクエストがセキュアかどうかを判断するには、ウェブサーバーからその情報
+をアップストリームのアプリケーションサーバーに送る必要があります。その情報を送る
+ために、ウェブサーバーを設定する必要があります。具体的にいうと、 `request.is_secure()`_
+に頼っているので、このメソッドが正しい値を返すようにします。
+
+nginx・FastCGIの場合
++++++++++++++++++++++++++++++
+
+nginxとFastCGIを使う場合、ウェブサーバーの設定で、FastCGIの ``HTTPS`` パラメーターを
+``on`` に設定すれば、 Django のアプリケーションはHTTPかHTTPSかを判断することが
+できます。そうするには、 ``fastcgi_param`` を ``HTTPS on`` に設定します。
+``HTTPS`` を ``'on'`` に設定すれば、flup は ``wsgi.url_scheme`` を ``https`` に設定し、
+`request.is_secure()`_ が正しい値を返すようになります。
+
+.. code-block:: nginx
+
+    location / {
+        include                 /etc/nginx/fastcgi_params;
+        fastcgi_pass            upstream;
+        fastcgi_param           HTTPS on;
+    }
+
+nginx・HTTP proxyの場合
++++++++++++++++++++++++++++++
+
+HTTPリバースプロクシーとして、nginx を使う場合、HTTPリクエストがセキュアか
+どうかの情報をHTTPヘッダーでアプリケーションに渡す必要があります。
+HTTPSで、送っているはずなのに、ブラウザがHTTPで情報を送らせてしまう
+man-in-the-middle 攻撃を避けるために、 HTTP リクエストの場合でも、nginxで
+このヘッダーを設定する、もしくは削る必要があります。
+
+:ref:`SSL_REQUEST_HEADER <setting-ssl-request-header>` で設定したヘッダー名と
+値を nginx 側で設定してください。
+:class:`SSLProxyMiddleware <beproud.django.ssl.middleware.SSLProxyMiddleware>`
+と併用します。
+
+.. code-block:: nginx
+
+    #HTTP
+    server {
+        listen 80;
+        location / {
+            proxy_pass          http://myproxy;    
+            
+            # We need to set this header for HTTP requests as well
+            # so that we won't fall victim to man-in-the-middle attacks.
+            proxy_set_header HTTP_X_FORWARDED_PROTOCOL      "http";
+            # ...
+        }
+    }
+     
+    # HTTPS
+    server {
+        listen 443;
+        ssl on;
+        # ...
+        location / {
+            proxy_pass          http://myproxy;    
+            # This should be set to the same headeras the
+            # non-ssl setup above.
+            proxy_set_header    HTTP_X_FORWARDED_PROTOCOL   https; 
+            # ...
+        }
+    }
+
+
+
+
+.. Apache・FastCGIの場合
+.. +++++++++++++++++++++++++++++
+.. 
+.. TODO: 後で書く
+
+Apache・mod_wsgiの場合
++++++++++++++++++++++++++++++
+
+Apache・mod_wsgi を使う場合は、 ``wsgi.url_scheme`` が設定していますので、
+特に特別な設定をせずに、 `request.is_secure()`_ が正しい値を返します。
+
+.. _`request.is_secure()`: http://djangoproject.jp/doc/ja/1.0/ref/request-response.html#django.http.HttpRequest.is_secure
+.. _`INSTALLED_APPS`: http://djangoproject.jp/doc/ja/1.0/ref/settings.html#installed-apps
+.. _`MIDDLEWARE_CLASSES`: http://djangoproject.jp/doc/ja/1.0/ref/settings.html#setting-MIDDLEWARE_CLASSES 

docs/ja/source/settings.rst

+:mod:`settings` -- bpssl 設定 
+================================================================
+
+.. _setting-ssl-urls:
+
+SSL_URLS
+---------------------
+
+SSL専用URLの正規表現のリストです。リクエストのURLがいずれかの正規表現にマッチしたら、
+:class:`SSLRedirectMiddleware <beproud.django.ssl.middleware.SSLRedirectMiddleware>`
+はそのURLをSSLのリクエストでしかアクセスできないようにする。HTTPでアクセスが来た場合、