1import os
2
3import cherrypy
4from cherrypy import tools
5from cherrypy.test import helper
6
7
8localDir = os.path.dirname(__file__)
9logfile = os.path.join(localDir, 'test_misc_tools.log')
10
11
12def setup_server():
13    class Root:
14
15        @cherrypy.expose
16        def index(self):
17            yield 'Hello, world'
18        h = [('Content-Language', 'en-GB'), ('Content-Type', 'text/plain')]
19        tools.response_headers(headers=h)(index)
20
21        @cherrypy.expose
22        @cherrypy.config(**{
23            'tools.response_headers.on': True,
24            'tools.response_headers.headers': [
25                ('Content-Language', 'fr'),
26                ('Content-Type', 'text/plain'),
27            ],
28            'tools.log_hooks.on': True,
29        })
30        def other(self):
31            return 'salut'
32
33    @cherrypy.config(**{'tools.accept.on': True})
34    class Accept:
35
36        @cherrypy.expose
37        def index(self):
38            return '<a href="feed">Atom feed</a>'
39
40        @cherrypy.expose
41        @tools.accept(media='application/atom+xml')
42        def feed(self):
43            return """<?xml version="1.0" encoding="utf-8"?>
44<feed xmlns="http://www.w3.org/2005/Atom">
45    <title>Unknown Blog</title>
46</feed>"""
47
48        @cherrypy.expose
49        def select(self):
50            # We could also write this: mtype = cherrypy.lib.accept.accept(...)
51            mtype = tools.accept.callable(['text/html', 'text/plain'])
52            if mtype == 'text/html':
53                return '<h2>Page Title</h2>'
54            else:
55                return 'PAGE TITLE'
56
57    class Referer:
58
59        @cherrypy.expose
60        def accept(self):
61            return 'Accepted!'
62        reject = accept
63
64    class AutoVary:
65
66        @cherrypy.expose
67        def index(self):
68            # Read a header directly with 'get'
69            cherrypy.request.headers.get('Accept-Encoding')
70            # Read a header directly with '__getitem__'
71            cherrypy.request.headers['Host']
72            # Read a header directly with '__contains__'
73            'If-Modified-Since' in cherrypy.request.headers
74            # Read a header directly
75            'Range' in cherrypy.request.headers
76            # Call a lib function
77            tools.accept.callable(['text/html', 'text/plain'])
78            return 'Hello, world!'
79
80    conf = {'/referer': {'tools.referer.on': True,
81                         'tools.referer.pattern': r'http://[^/]*example\.com',
82                         },
83            '/referer/reject': {'tools.referer.accept': False,
84                                'tools.referer.accept_missing': True,
85                                },
86            '/autovary': {'tools.autovary.on': True},
87            }
88
89    root = Root()
90    root.referer = Referer()
91    root.accept = Accept()
92    root.autovary = AutoVary()
93    cherrypy.tree.mount(root, config=conf)
94    cherrypy.config.update({'log.error_file': logfile})
95
96
97class ResponseHeadersTest(helper.CPWebCase):
98    setup_server = staticmethod(setup_server)
99
100    def testResponseHeadersDecorator(self):
101        self.getPage('/')
102        self.assertHeader('Content-Language', 'en-GB')
103        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
104
105    def testResponseHeaders(self):
106        self.getPage('/other')
107        self.assertHeader('Content-Language', 'fr')
108        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
109
110
111class RefererTest(helper.CPWebCase):
112    setup_server = staticmethod(setup_server)
113
114    def testReferer(self):
115        self.getPage('/referer/accept')
116        self.assertErrorPage(403, 'Forbidden Referer header.')
117
118        self.getPage('/referer/accept',
119                     headers=[('Referer', 'http://www.example.com/')])
120        self.assertStatus(200)
121        self.assertBody('Accepted!')
122
123        # Reject
124        self.getPage('/referer/reject')
125        self.assertStatus(200)
126        self.assertBody('Accepted!')
127
128        self.getPage('/referer/reject',
129                     headers=[('Referer', 'http://www.example.com/')])
130        self.assertErrorPage(403, 'Forbidden Referer header.')
131
132
133class AcceptTest(helper.CPWebCase):
134    setup_server = staticmethod(setup_server)
135
136    def test_Accept_Tool(self):
137        # Test with no header provided
138        self.getPage('/accept/feed')
139        self.assertStatus(200)
140        self.assertInBody('<title>Unknown Blog</title>')
141
142        # Specify exact media type
143        self.getPage('/accept/feed',
144                     headers=[('Accept', 'application/atom+xml')])
145        self.assertStatus(200)
146        self.assertInBody('<title>Unknown Blog</title>')
147
148        # Specify matching media range
149        self.getPage('/accept/feed', headers=[('Accept', 'application/*')])
150        self.assertStatus(200)
151        self.assertInBody('<title>Unknown Blog</title>')
152
153        # Specify all media ranges
154        self.getPage('/accept/feed', headers=[('Accept', '*/*')])
155        self.assertStatus(200)
156        self.assertInBody('<title>Unknown Blog</title>')
157
158        # Specify unacceptable media types
159        self.getPage('/accept/feed', headers=[('Accept', 'text/html')])
160        self.assertErrorPage(406,
161                             'Your client sent this Accept header: text/html. '
162                             'But this resource only emits these media types: '
163                             'application/atom+xml.')
164
165        # Test resource where tool is 'on' but media is None (not set).
166        self.getPage('/accept/')
167        self.assertStatus(200)
168        self.assertBody('<a href="feed">Atom feed</a>')
169
170    def test_accept_selection(self):
171        # Try both our expected media types
172        self.getPage('/accept/select', [('Accept', 'text/html')])
173        self.assertStatus(200)
174        self.assertBody('<h2>Page Title</h2>')
175        self.getPage('/accept/select', [('Accept', 'text/plain')])
176        self.assertStatus(200)
177        self.assertBody('PAGE TITLE')
178        self.getPage('/accept/select',
179                     [('Accept', 'text/plain, text/*;q=0.5')])
180        self.assertStatus(200)
181        self.assertBody('PAGE TITLE')
182
183        # text/* and */* should prefer text/html since it comes first
184        # in our 'media' argument to tools.accept
185        self.getPage('/accept/select', [('Accept', 'text/*')])
186        self.assertStatus(200)
187        self.assertBody('<h2>Page Title</h2>')
188        self.getPage('/accept/select', [('Accept', '*/*')])
189        self.assertStatus(200)
190        self.assertBody('<h2>Page Title</h2>')
191
192        # Try unacceptable media types
193        self.getPage('/accept/select', [('Accept', 'application/xml')])
194        self.assertErrorPage(
195            406,
196            'Your client sent this Accept header: application/xml. '
197            'But this resource only emits these media types: '
198            'text/html, text/plain.')
199
200
201class AutoVaryTest(helper.CPWebCase):
202    setup_server = staticmethod(setup_server)
203
204    def testAutoVary(self):
205        self.getPage('/autovary/')
206        self.assertHeader(
207            'Vary',
208            'Accept, Accept-Charset, Accept-Encoding, '
209            'Host, If-Modified-Since, Range'
210        )
211