| 57 | | |
|---|
| 58 | | |
|---|
| 59 | | # Internal Redirect # |
|---|
| 60 | | |
|---|
| 61 | | |
|---|
| 62 | | class InternalRedirector(object): |
|---|
| 63 | | """WSGI middleware which handles cherrypy.InternalRedirect. |
|---|
| 64 | | |
|---|
| 65 | | When cherrypy.InternalRedirect is raised, this middleware traps it, |
|---|
| 66 | | rewrites the WSGI environ using the new path and query_string, |
|---|
| 67 | | and calls the next application again. Because the wsgi.input stream |
|---|
| 68 | | may have already been consumed by the next application, the redirected |
|---|
| 69 | | call will always be of HTTP method "GET", and therefore any params must |
|---|
| 70 | | be passed in the InternalRedirect object's query_string attribute. |
|---|
| 71 | | If you need something more complicated, make and raise your own |
|---|
| 72 | | exception and your own WSGI middleware to trap it. ;) |
|---|
| 73 | | |
|---|
| 74 | | It would be a bad idea to raise InternalRedirect after you've already |
|---|
| 75 | | yielded response content, although an enterprising soul could choose |
|---|
| 76 | | to abuse this. |
|---|
| 77 | | |
|---|
| 78 | | nextapp: the next application callable in the WSGI chain. |
|---|
| 79 | | |
|---|
| 80 | | recursive: if False (the default), each URL (path + qs) will be |
|---|
| 81 | | stored, and, if the same URL is requested again, RuntimeError will |
|---|
| 82 | | be raised. If 'recursive' is True, no such error will be raised. |
|---|
| 83 | | """ |
|---|
| 84 | | |
|---|
| 85 | | def __init__(self, nextapp, recursive=False): |
|---|
| 86 | | self.nextapp = nextapp |
|---|
| 87 | | self.recursive = recursive |
|---|
| 88 | | |
|---|
| 89 | | def __call__(self, environ, start_response): |
|---|
| 90 | | return IRResponse(self.nextapp, environ, start_response, self.recursive) |
|---|
| 91 | | |
|---|
| 92 | | |
|---|
| 93 | | class IRResponse(object): |
|---|
| 94 | | |
|---|
| 95 | | def __init__(self, nextapp, environ, start_response, recursive): |
|---|
| 96 | | self.redirections = [] |
|---|
| 97 | | self.recursive = recursive |
|---|
| 98 | | self.environ = environ.copy() |
|---|
| 99 | | self.nextapp = nextapp |
|---|
| 100 | | self.start_response = start_response |
|---|
| 101 | | self.response = None |
|---|
| 102 | | self.iter_response = None |
|---|
| 103 | | self.setapp() |
|---|
| 104 | | |
|---|
| 105 | | def setapp(self): |
|---|
| 106 | | while True: |
|---|
| 107 | | try: |
|---|
| 108 | | self.response = self.nextapp(self.environ, self.start_response) |
|---|
| 109 | | self.iter_response = iter(self.response) |
|---|
| 110 | | return |
|---|
| 111 | | except _cherrypy.InternalRedirect, ir: |
|---|
| 112 | | self.close() |
|---|
| 113 | | self.setenv(ir) |
|---|
| 114 | | |
|---|
| 115 | | def setenv(self, ir): |
|---|
| 116 | | env = self.environ |
|---|
| 117 | | if not self.recursive: |
|---|
| 118 | | if ir.path in self.redirections: |
|---|
| 119 | | raise RuntimeError("InternalRedirector visited the " |
|---|
| 120 | | "same URL twice: %r" % ir.path) |
|---|
| 121 | | else: |
|---|
| 122 | | # Add the *previous* path_info + qs to redirections. |
|---|
| 123 | | sn = env.get('SCRIPT_NAME', '') |
|---|
| 124 | | path = env.get('PATH_INFO', '') |
|---|
| 125 | | qs = env.get('QUERY_STRING', '') |
|---|
| 126 | | if qs: |
|---|
| 127 | | qs = "?" + qs |
|---|
| 128 | | self.redirections.append(_http.urljoin(sn, path) + qs) |
|---|
| 129 | | |
|---|
| 130 | | # Munge environment and try again. |
|---|
| 131 | | env['REQUEST_METHOD'] = "GET" |
|---|
| 132 | | env['PATH_INFO'] = ir.path |
|---|
| 133 | | env['QUERY_STRING'] = ir.query_string |
|---|
| 134 | | env['wsgi.input'] = _StringIO.StringIO() |
|---|
| 135 | | env['CONTENT_LENGTH'] = "0" |
|---|
| 136 | | |
|---|
| 137 | | def close(self): |
|---|
| 138 | | if hasattr(self.response, "close"): |
|---|
| 139 | | self.response.close() |
|---|
| 140 | | |
|---|
| 141 | | def __iter__(self): |
|---|
| 142 | | return self |
|---|
| 143 | | |
|---|
| 144 | | def next(self): |
|---|
| 145 | | while True: |
|---|
| 146 | | try: |
|---|
| 147 | | return self.iter_response.next() |
|---|
| 148 | | except _cherrypy.InternalRedirect, ir: |
|---|
| 149 | | self.close() |
|---|
| 150 | | self.setenv(ir) |
|---|
| 151 | | self.setapp() |
|---|