2 This module should only be used for debugging SSI statements.
4 Using DebugUnSsiMiddleware in production defeats the purpose of using SSI
5 in the first place, and is unsafe. You should use a proper webserver with SSI
6 support as a proxy (i.e. Nginx with ssi=on).
11 from django.core.urlresolvers import resolve
12 from ssify import DEBUG_VERBOSE
15 SSI_SET = re.compile(r"<!--#set var='(?P<var>[^']+)' "
16 r"value='(?P<value>|\\\\|.*?[^\\](?:\\\\)*)'-->", re.S)
17 SSI_ECHO = re.compile(r"<!--#echo var='(?P<var>[^']+)' encoding='none'-->")
18 SSI_INCLUDE = re.compile(r"<!--#include (?:virtual|file)='(?P<path>[^']+)'-->")
19 SSI_IF = re.compile(r"(?P<header><!--#if expr='(?P<expr>[^']*)'-->)"
20 r"(?P<value>.*?)(?:<!--#else-->(?P<else>.*?))?"
21 r"<!--#endif-->", re.S)
23 SSI_VAR = re.compile(r"\$\{(?P<var>.+)\}") # TODO: escaped?
26 class DebugUnSsiMiddleware(object):
28 Emulates a webserver with SSI support.
30 This middleware should only be used for debugging purposes.
31 SsiMiddleware will enable it automatically, if SSIFY_DEBUG setting
32 is set to True, so you don't normally need to include it in
35 If SSIFY_DEBUG_VERBOSE setting is True, it will also leave some
36 information in HTML comments.
40 def _process_rendered_response(request, response):
41 """Recursively process SSI statements in the response."""
42 def ssi_include(match):
43 """Replaces SSI include with contents rendered by relevant view."""
44 path = process_value(match.group('path'))
45 func, args, kwargs = resolve(path)
46 parsed = urlparse.urlparse(path)
47 request.META['PATH_INFO'] = request.path_info = \
48 request.path = parsed.path
49 request.META['QUERY_STRING'] = parsed.query
50 content = func(request, *args, **kwargs).content
51 content = process_content(content)
56 match.group(0).replace('<!--#', '<!--#end-'),
62 """Interprets SSI set statement."""
63 variables[match.group('var')] = match.group('value')
70 """Interprets SSI echo, outputting the value of the variable."""
71 content = variables[match.group('var')]
76 match.group(0).replace('<!--#', '<!--#end-'),
82 """Interprets SSI if statement."""
83 expr = process_value(match.group('expr'))
85 content = match.group('value')
87 content = match.group('else')
90 match.group('header'),
92 match.group('header').replace('<!--#', '<!--#end-'),
98 """Resolves ${var}-style variable reference."""
99 return variables[match.group('var')]
101 def process_value(content):
102 """Resolves any ${var}-style variable references in the content."""
103 return re.sub(SSI_VAR, ssi_var, content)
105 def process_content(content):
106 """Interprets SSI statements in the content."""
107 content = re.sub(SSI_SET, ssi_set, content)
108 content = re.sub(SSI_ECHO, ssi_echo, content)
109 content = re.sub(SSI_IF, ssi_if, content)
110 content = re.sub(SSI_INCLUDE, ssi_include, content)
114 response.content = process_content(response.content)
115 response['Content-Length'] = len(response.content)
117 def process_response(self, request, response):
118 """Support for unrendered responses."""
119 if hasattr(response, 'render') and callable(response.render):
120 response.add_post_render_callback(
121 lambda r: self._process_rendered_response(request, r)
124 self._process_rendered_response(request, response)