0001"""
0002Takes a response (headers + content) and relocates it, changing domain
0003names and paths.
0004"""
0005import urlparse
0006import re
0007from paste.request import construct_url
0008from paste.response import header_value
0009import fixuplinks
0010
0011def relocate_response(headers, content, base_href, old_href, new_href):
0012    """
0013    Takes headers and content, and replaces all instances of old_href
0014    with new_href.  Returns (new_headers, new_content)
0015    """
0016    new_headers = relocate_headers(headers, base_href, old_href, new_href)
0017    new_content = relocate_content(content, base_href, old_href, new_href)
0018    return new_headers, new_content
0019
0020def relocate_headers(headers, base_href, old_href, new_href):
0021    new_headers = []
0022    for name, value in headers:
0023        if name.lower() == 'location':
0024            value = relocate_href(value, base_href, old_href, new_href)
0025        if name.lower() == 'set-cookie':
0026            if 'domain' in value:
0027                # We have to rewrite the domain
0028                old_domain = urlparse.urlsplit(old_href)[1].split(':')[0]
0029                new_domain = urlparse.urlsplit(new_href)[1].split(':')[0]
0030                def repl(match):
0031                    return match.group(1)+new_domain
0032                value = re.sub(
0033                    r'(domain=[^ ",]*)%s' % re.escape(old_domain),
0034                    repl, value)
0035        new_headers.append((name, value))
0036    return new_headers
0037
0038def relocate_content(content, base_href, old_href, new_href):
0039    def sub_link(href):
0040        return relocate_href(href, base_href, old_href, new_href)
0041    return fixuplinks.fixup_text_links(content, sub_link)
0042
0043# This catches the case of http://foo, which is equivalent to
0044# http://foo/ :
0045_domain_no_slash_re = re.compile(r'^[a-z]+://[^/]+$', re.I)
0046
0047def relocate_href(href, base_href, old_href, new_href):
0048    real_href = urlparse.urljoin(base_href, href)
0049    if _domain_no_slash_re.search(real_href):
0050        real_href += '/'
0051    if not real_href.startswith(old_href):
0052        return href
0053    return new_href + real_href[len(old_href):]
0054
0055class RelocateMiddleware(object):
0056
0057    def __init__(self, app, old_href):
0058        self.app = app
0059        if old_href.endswith(':80'):
0060            old_href = old_href[:-3]
0061        self.old_href = old_href
0062
0063    def __call__(self, environ, start_response):
0064        new_href = construct_url(environ, path_info='')
0065        base_href = construct_url(environ)
0066        skipped = []
0067        written = []
0068        stat_headers = []
0069        def repl_start_response(status, headers, exc_info=None):
0070            headers = relocate_headers(headers, base_href, self.old_href, new_href)
0071            content_type = header_value(headers, 'content-type')
0072            if not content_type or not content_type.startswith('text/html'):
0073                skipped.append(True)
0074                return start_response(status, headers, exc_info)
0075            stat_headers[:] = [status, headers]
0076            return written.append
0077        app_iter = self.app(environ, repl_start_response)
0078        if skipped:
0079            return app_iter
0080        start_response(*stat_headers)
0081        try:
0082            for chunk in app_iter:
0083                written.append(chunk)
0084        finally:
0085            if hasattr(app_iter, 'close'):
0086                app_iter.close()
0087        content = ''.join(written)
0088        content = relocate_content(content, base_href, self.old_href, new_href)
0089        return [content]