Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/test/test_custom_filters.py

Revision 1582 (checked in by dowski, 2 years ago)

2.x backport of fixes in [1388] and [1530] (safer WSGI and request close). See #567.

Also buglet fixes in test.py and helper.py.

  • Property svn:eol-style set to native
Line 
1 """Test the various means of instantiating and invoking filters."""
2
3 import time
4 import types
5 import test
6 test.prefer_parent_path()
7
8 import cherrypy
9 from cherrypy import filters
10 from cherrypy.filters.basefilter import BaseFilter
11
12
13 class AccessFilter(BaseFilter):
14    
15     def before_request_body(self):
16         if not cherrypy.config.get("access_filter.on", False):
17             return
18        
19         if not getattr(cherrypy.request, "login", None):
20             raise cherrypy.HTTPError(401)
21
22
23 def setup_server():
24
25     class Numerify(BaseFilter):
26        
27         def on_start_resource(self):
28             m = cherrypy.config.get("numerify_filter.map", {})
29             cherrypy.request.numerify_map = m.items()
30        
31         def before_finalize(self):
32             if not cherrypy.config.get("numerify_filter.on", False):
33                 return
34            
35             def number_it(body):
36                 for chunk in body:
37                     for k, v in cherrypy.request.numerify_map:
38                         chunk = chunk.replace(k, v)
39                     yield chunk
40             cherrypy.response.body = number_it(cherrypy.response.body)
41    
42    
43     # It's not mandatory to inherit from BaseFilter.
44     class NadsatFilter:
45        
46         def __init__(self):
47             self.counter = 0
48             self.ended = {}
49        
50         def before_main(self):
51             cherrypy.request.counter = self.counter = self.counter + 1
52             self.ended[cherrypy.request.counter] = False
53        
54         def before_finalize(self):
55             def nadsat_it_up(body):
56                 for chunk in body:
57                     chunk = chunk.replace("good", "horrorshow")
58                     chunk = chunk.replace("piece", "lomtick")
59                     yield chunk
60             cherrypy.response.body = nadsat_it_up(cherrypy.response.body)
61        
62         def on_end_request(self):
63             # This runs after the request has been completely written out.
64             cherrypy.response.body = "razdrez"
65             self.ended[cherrypy.request.counter] = True
66
67
68
69     class Root:
70         def index(self):
71             return "Howdy earth!"
72         index.exposed = True
73
74     cherrypy.root = Root()
75
76
77     class TestType(type):
78         """Metaclass which automatically exposes all functions in each subclass,
79         and adds an instance of the subclass as an attribute of cherrypy.root.
80         """
81         def __init__(cls, name, bases, dct):
82             type.__init__(name, bases, dct)
83             for value in dct.itervalues():
84                 if isinstance(value, types.FunctionType):
85                     value.exposed = True
86             setattr(cherrypy.root, name.lower(), cls())
87     class Test(object):
88         __metaclass__ = TestType
89
90
91     class CPFilterList(Test):
92        
93         # METHOD ONE:
94         # Use _cp_filters (old name: _cpFilterList)
95         _cp_filters = [NadsatFilter()]
96        
97         def index(self):
98             return "A good piece of cherry pie"
99        
100         def ended(self, id):
101             return repr(self._cp_filters[0].ended[int(id)])
102        
103         def err(self):
104             raise ValueError()
105        
106         def stream(self):
107             for i in xrange(100000000):
108                 yield str(i)
109        
110         def errinstream(self):
111             raise ValueError()
112             yield "confidential"
113        
114         def restricted(self):
115             return "Welcome!"
116        
117         def err_in_onstart(self):
118             return "success!"
119
120
121     cherrypy.config.update({
122         'global': {
123             # METHOD TWO:
124             # Declare a classname in server.input_filters.
125             'server.input_filters': ["cherrypy.test.test_custom_filters.AccessFilter"],
126             'server.log_to_screen': False,
127             'server.environment': 'production',
128             'server.show_tracebacks': True,
129         },
130         '/cpfilterlist': {
131             'numerify_filter.on': True,
132             'numerify_filter.map': {"pie": "3.14159"}
133         },
134         '/cpfilterlist/restricted': {
135             'access_filter.on': True,
136             'server.show_tracebacks': False,
137         },
138         '/cpfilterlist/errinstream': {
139             'stream_response': True,
140         },
141         '/cpfilterlist/stream': {
142             'stream_response': True,
143         },
144         '/cpfilterlist/err_in_onstart': {
145             # Because this isn't a dict, on_start_resource will error.
146             'numerify_filter.map': "pie->3.14159"
147         },
148     })
149
150     # METHOD THREE:
151     # Insert a class directly into the filters.output_filters chain.
152     # You can also insert a string, but we're effectively testing
153     # using-a-string via the config file.
154     filters.input_filters.insert(0, Numerify)
155     filters.output_filters.insert(0, Numerify)
156
157     # We have to call filters.init() here (if we want methods #2 and #3
158     # to work), because the test suite may already have run server.start()
159     # (which is where filters.init() is usually called).
160     filters.init()
161
162
163 #                             Client-side code                             #
164
165 import helper
166
167
168 class FilterTests(helper.CPWebCase):
169     def testCPFilterList(self):
170         self.getPage("/cpfilterlist/")
171         # If body is "razdrez", then on_end_request is being called too early.
172         self.assertBody("A horrorshow lomtick of cherry 3.14159")
173         # If this fails, then on_end_request isn't being called at all.
174         self.getPage("/cpfilterlist/ended/1")
175         self.assertBody("True")
176        
177         valerr = '\n    raise ValueError()\nValueError'
178         self.getPage("/cpfilterlist/err")
179         # If body is "razdrez", then on_end_request is being called too early.
180         self.assertErrorPage(500, pattern=valerr)
181         # If this fails, then on_end_request isn't being called at all.
182         self.getPage("/cpfilterlist/ended/3")
183         self.assertBody("True")
184        
185         # If body is "razdrez", then on_end_request is being called too early.
186         self.getPage("/cpfilterlist/errinstream")
187         # Because this error is raised after the response body has
188         # started, the status should not change to an error status.
189         self.assertStatus("200 OK")
190         self.assertBody("Unrecoverable error in the server.")
191         # If this fails, then on_end_request isn't being called at all.
192         self.getPage("/cpfilterlist/ended/5")
193         self.assertBody("True")
194
195         # Test that on_end_request is called even if the client drops.
196         self.persistent = True
197         try:
198             conn = self.HTTP_CONN
199             conn.putrequest('GET', '/cpfilterlist/stream', skip_host=True)
200             conn.putheader('Host', self.HOST)
201             conn.endheaders()
202             # Skip the rest of the request and close the conn. This will
203             # cause the server's active socket to error, which *should*
204             # result in the request being aborted, and request.close being
205             # called all the way up the stack (including WSGI middleware),
206             # eventually calling our on_end_request hook.
207         finally:
208             self.persistent = False
209         time.sleep(0.1)
210         # on_end_request should have been called
211         self.getPage('/cpfilterlist/ended/7')
212         self.assertBody("True")
213        
214         # Test the config method.
215         self.getPage("/cpfilterlist/restricted")
216         self.assertErrorPage(401)
217    
218     def testGuaranteedFilters(self):
219         # The on_start_resource and on_end_request filter methods are all
220         # guaranteed to run, even if there are failures in other on_start
221         # or on_end methods. This is NOT true of the other filter methods.
222         # Here, we have set up a failure in NumerifyFilter.on_start_resource,
223         # but because that failure is logged and passed over, the error
224         # page we obtain in the user agent should be from before_finalize.
225         self.getPage("/cpfilterlist/err_in_onstart")
226         self.assertErrorPage(500)
227         self.assertInBody("AttributeError: 'Request' object has no "
228                           "attribute 'numerify_map'")
229
230
231 if __name__ == '__main__':
232     setup_server()
233     helper.testmain()
234
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets