~ubuntu-branches/debian/jessie/cherrypy3/jessie

« back to all changes in this revision

Viewing changes to cherrypy/test/test_static.py

  • Committer: Package Import Robot
  • Author(s): Gustavo Noronha Silva, JCF Ploemen, Stéphane Graber, Gustavo Noronha
  • Date: 2012-01-06 10:13:27 UTC
  • mfrom: (1.1.4) (7.1.2 sid)
  • Revision ID: package-import@ubuntu.com-20120106101327-smxnhguqs14ubl7e
Tags: 3.2.2-1
[ JCF Ploemen ]
* New upstream release (Closes: #571196).
* Bumped Standards-Version to 3.8.4 (no changes needed).
* Removing patch 02: no longer needed, incorporated upstream.
* Updating patch 00 to match release.
* Install cherryd man page via debian/manpages.
* debian/copyright:
  + Added notice for cherrypy/lib/httpauth.py.
  + Fixed years.
* debian/watch:
  + Don't hit on the -py3 release by blocking '-' from the version.
  + Mangle upstream version, inserting a tilde for beta/rc.

[ Stéphane Graber <stgraber@ubuntu.com> ]
 * Convert from python-support to dh_python2 (#654375)
  - debian/pyversions: Removed (no longer needed)
  - debian/rules
   + Replace call to dh_pysupport by dh_python2
   + Add --with=python2 to all dh calls
  - debian/control
   + Drop build-depends on python-support
   + Bump build-depends on python-all to >= 2.6.6-3~
   + Replace XS-Python-Version by X-Python-Version
   + Remove XB-Python-Version from binary package

[ Gustavo Noronha ]
* debian/control, debian/rules, debian/manpages:
 - use help2man to generate a manpage for cherryd at build time, since
  one is no longer shipped along with the source code
* debian/control:
- add python-nose to Build-Depends, since it's used during the
  documentation build for cross-reference generation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from cherrypy.test import test
2
 
test.prefer_parent_path()
 
1
from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, ntob
 
2
from cherrypy._cpcompat import BytesIO
3
3
 
4
4
import os
5
5
curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
6
6
has_space_filepath = os.path.join(curdir, 'static', 'has space.html')
 
7
bigfile_filepath = os.path.join(curdir, "static", "bigfile.log")
 
8
BIGFILE_SIZE = 1024 * 1024
7
9
import threading
8
10
 
9
11
import cherrypy
10
 
 
11
 
def setup_server():
12
 
    if not os.path.exists(has_space_filepath):
13
 
        file(has_space_filepath, 'wb').write('Hello, world\r\n')
14
 
        
15
 
    class Root:
16
 
        pass
17
 
 
18
 
    class Static:
19
 
        
20
 
        def index(self):
21
 
            return 'You want the Baron? You can have the Baron!'
22
 
        index.exposed = True
23
 
        
24
 
        def dynamic(self):
25
 
            return "This is a DYNAMIC page"
26
 
        dynamic.exposed = True
27
 
    
28
 
    
29
 
    cherrypy.config.update({'environment': 'test_suite'})
30
 
    
31
 
    root = Root()
32
 
    root.static = Static()
33
 
    
34
 
    rootconf = {
35
 
        '/static': {
36
 
            'tools.staticdir.on': True,
37
 
            'tools.staticdir.dir': 'static',
38
 
            'tools.staticdir.root': curdir,
39
 
        },
40
 
        '/style.css': {
41
 
            'tools.staticfile.on': True,
42
 
            'tools.staticfile.filename': os.path.join(curdir, 'style.css'),
43
 
        },
44
 
        '/docroot': {
45
 
            'tools.staticdir.on': True,
46
 
            'tools.staticdir.root': curdir,
47
 
            'tools.staticdir.dir': 'static',
48
 
            'tools.staticdir.index': 'index.html',
49
 
        },
50
 
        '/error': {
51
 
            'tools.staticdir.on': True,
52
 
            'request.show_tracebacks': True,
53
 
        },
54
 
        }
55
 
    rootApp = cherrypy.Application(root)
56
 
    rootApp.merge(rootconf)
57
 
    
58
 
    test_app_conf = {
59
 
        '/test': {
60
 
            'tools.staticdir.index': 'index.html',
61
 
            'tools.staticdir.on': True,
62
 
            'tools.staticdir.root': curdir,
63
 
            'tools.staticdir.dir': 'static',
64
 
            },
65
 
        }
66
 
    testApp = cherrypy.Application(Static())
67
 
    testApp.merge(test_app_conf)
68
 
    
69
 
    vhost = cherrypy._cpwsgi.VirtualHost(rootApp, {'virt.net': testApp})
70
 
    cherrypy.tree.graft(vhost)
71
 
 
72
 
 
73
 
def teardown_server():
74
 
    if os.path.exists(has_space_filepath):
75
 
        try:
76
 
            os.unlink(has_space_filepath)
77
 
        except:
78
 
            pass
79
 
        
 
12
from cherrypy.lib import static
80
13
from cherrypy.test import helper
81
14
 
 
15
 
82
16
class StaticTest(helper.CPWebCase):
 
17
 
 
18
    def setup_server():
 
19
        if not os.path.exists(has_space_filepath):
 
20
            open(has_space_filepath, 'wb').write(ntob('Hello, world\r\n'))
 
21
        if not os.path.exists(bigfile_filepath):
 
22
            open(bigfile_filepath, 'wb').write(ntob("x" * BIGFILE_SIZE))
 
23
        
 
24
        class Root:
 
25
            
 
26
            def bigfile(self):
 
27
                from cherrypy.lib import static
 
28
                self.f = static.serve_file(bigfile_filepath)
 
29
                return self.f
 
30
            bigfile.exposed = True
 
31
            bigfile._cp_config = {'response.stream': True}
 
32
            
 
33
            def tell(self):
 
34
                if self.f.input.closed:
 
35
                    return ''
 
36
                return repr(self.f.input.tell()).rstrip('L')
 
37
            tell.exposed = True
 
38
            
 
39
            def fileobj(self):
 
40
                f = open(os.path.join(curdir, 'style.css'), 'rb')
 
41
                return static.serve_fileobj(f, content_type='text/css')
 
42
            fileobj.exposed = True
 
43
            
 
44
            def bytesio(self):
 
45
                f = BytesIO(ntob('Fee\nfie\nfo\nfum'))
 
46
                return static.serve_fileobj(f, content_type='text/plain')
 
47
            bytesio.exposed = True
 
48
        
 
49
        class Static:
 
50
            
 
51
            def index(self):
 
52
                return 'You want the Baron? You can have the Baron!'
 
53
            index.exposed = True
 
54
            
 
55
            def dynamic(self):
 
56
                return "This is a DYNAMIC page"
 
57
            dynamic.exposed = True
 
58
        
 
59
        
 
60
        root = Root()
 
61
        root.static = Static()
 
62
        
 
63
        rootconf = {
 
64
            '/static': {
 
65
                'tools.staticdir.on': True,
 
66
                'tools.staticdir.dir': 'static',
 
67
                'tools.staticdir.root': curdir,
 
68
            },
 
69
            '/style.css': {
 
70
                'tools.staticfile.on': True,
 
71
                'tools.staticfile.filename': os.path.join(curdir, 'style.css'),
 
72
            },
 
73
            '/docroot': {
 
74
                'tools.staticdir.on': True,
 
75
                'tools.staticdir.root': curdir,
 
76
                'tools.staticdir.dir': 'static',
 
77
                'tools.staticdir.index': 'index.html',
 
78
            },
 
79
            '/error': {
 
80
                'tools.staticdir.on': True,
 
81
                'request.show_tracebacks': True,
 
82
            },
 
83
            }
 
84
        rootApp = cherrypy.Application(root)
 
85
        rootApp.merge(rootconf)
 
86
        
 
87
        test_app_conf = {
 
88
            '/test': {
 
89
                'tools.staticdir.index': 'index.html',
 
90
                'tools.staticdir.on': True,
 
91
                'tools.staticdir.root': curdir,
 
92
                'tools.staticdir.dir': 'static',
 
93
                },
 
94
            }
 
95
        testApp = cherrypy.Application(Static())
 
96
        testApp.merge(test_app_conf)
 
97
        
 
98
        vhost = cherrypy._cpwsgi.VirtualHost(rootApp, {'virt.net': testApp})
 
99
        cherrypy.tree.graft(vhost)
 
100
    setup_server = staticmethod(setup_server)
 
101
 
 
102
 
 
103
    def teardown_server():
 
104
        for f in (has_space_filepath, bigfile_filepath):
 
105
            if os.path.exists(f):
 
106
                try:
 
107
                    os.unlink(f)
 
108
                except:
 
109
                    pass
 
110
    teardown_server = staticmethod(teardown_server)
 
111
 
83
112
    
84
113
    def testStatic(self):
85
114
        self.getPage("/static/index.html")
116
145
        # Check a directory via fall-through to dynamic handler.
117
146
        self.getPage("/static/")
118
147
        self.assertStatus('200 OK')
119
 
        self.assertHeader('Content-Type', 'text/html')
 
148
        self.assertHeader('Content-Type', 'text/html;charset=utf-8')
120
149
        self.assertBody('You want the Baron? You can have the Baron!')
121
150
    
122
151
    def test_index(self):
127
156
        self.assertBody('Hello, world\r\n')
128
157
        # The same page should be returned even if redirected.
129
158
        self.getPage("/docroot")
130
 
        self.assertStatus((302, 303))
 
159
        self.assertStatus(301)
131
160
        self.assertHeader('Location', '%s/docroot/' % self.base())
132
 
        self.assertMatchesBody("This resource .* at <a href='%s/docroot/'>"
 
161
        self.assertMatchesBody("This resource .* <a href='%s/docroot/'>"
133
162
                               "%s/docroot/</a>." % (self.base(), self.base()))
134
163
    
135
164
    def test_config_errors(self):
136
165
        # Check that we get an error if no .file or .dir
137
166
        self.getPage("/error/thing.html")
138
167
        self.assertErrorPage(500)
139
 
        self.assertInBody("TypeError: staticdir() takes at least 2 "
140
 
                          "arguments (0 given)")
 
168
        self.assertMatchesBody(ntob("TypeError: staticdir\(\) takes at least 2 "
 
169
                                    "(positional )?arguments \(0 given\)"))
141
170
    
142
171
    def test_security(self):
143
172
        # Test up-level security
164
193
        self.getPage("/test/", [('Host', 'virt.net')])
165
194
        self.assertStatus(200)
166
195
        self.getPage("/test", [('Host', 'virt.net')])
167
 
        self.assertStatus((302, 303))
 
196
        self.assertStatus(301)
168
197
        self.assertHeader('Location', self.scheme + '://virt.net/test/')
169
 
 
170
 
 
171
 
if __name__ == "__main__":
172
 
    setup_server()
173
 
    helper.testmain()
 
198
    
 
199
    def test_serve_fileobj(self):
 
200
        self.getPage("/fileobj")
 
201
        self.assertStatus('200 OK')
 
202
        self.assertHeader('Content-Type', 'text/css;charset=utf-8')
 
203
        self.assertMatchesBody('^Dummy stylesheet')
 
204
    
 
205
    def test_serve_bytesio(self):
 
206
        self.getPage("/bytesio")
 
207
        self.assertStatus('200 OK')
 
208
        self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
 
209
        self.assertHeader('Content-Length', 14)
 
210
        self.assertMatchesBody('Fee\nfie\nfo\nfum')
 
211
    
 
212
    def test_file_stream(self):
 
213
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
214
            return self.skip()
 
215
        
 
216
        self.PROTOCOL = "HTTP/1.1"
 
217
        
 
218
        # Make an initial request
 
219
        self.persistent = True
 
220
        conn = self.HTTP_CONN
 
221
        conn.putrequest("GET", "/bigfile", skip_host=True)
 
222
        conn.putheader("Host", self.HOST)
 
223
        conn.endheaders()
 
224
        response = conn.response_class(conn.sock, method="GET")
 
225
        response.begin()
 
226
        self.assertEqual(response.status, 200)
 
227
        
 
228
        body = ntob('')
 
229
        remaining = BIGFILE_SIZE
 
230
        while remaining > 0:
 
231
            data = response.fp.read(65536)
 
232
            if not data:
 
233
                break
 
234
            body += data
 
235
            remaining -= len(data)
 
236
            
 
237
            if self.scheme == "https":
 
238
                newconn = HTTPSConnection
 
239
            else:
 
240
                newconn = HTTPConnection
 
241
            s, h, b = helper.webtest.openURL(
 
242
                ntob("/tell"), headers=[], host=self.HOST, port=self.PORT,
 
243
                http_conn=newconn)
 
244
            if not b:
 
245
                # The file was closed on the server.
 
246
                tell_position = BIGFILE_SIZE
 
247
            else:
 
248
                tell_position = int(b)
 
249
            
 
250
            expected = len(body)
 
251
            if tell_position >= BIGFILE_SIZE:
 
252
                # We can't exactly control how much content the server asks for.
 
253
                # Fudge it by only checking the first half of the reads.
 
254
                if expected < (BIGFILE_SIZE / 2):
 
255
                    self.fail(
 
256
                        "The file should have advanced to position %r, but has "
 
257
                        "already advanced to the end of the file. It may not be "
 
258
                        "streamed as intended, or at the wrong chunk size (64k)" %
 
259
                        expected)
 
260
            elif tell_position < expected:
 
261
                self.fail(
 
262
                    "The file should have advanced to position %r, but has "
 
263
                    "only advanced to position %r. It may not be streamed "
 
264
                    "as intended, or at the wrong chunk size (65536)" %
 
265
                    (expected, tell_position))
 
266
        
 
267
        if body != ntob("x" * BIGFILE_SIZE):
 
268
            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
 
269
                      (BIGFILE_SIZE, body[:50], len(body)))
 
270
        conn.close()
 
271
    
 
272
    def test_file_stream_deadlock(self):
 
273
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
274
            return self.skip()
 
275
        
 
276
        self.PROTOCOL = "HTTP/1.1"
 
277
        
 
278
        # Make an initial request but abort early.
 
279
        self.persistent = True
 
280
        conn = self.HTTP_CONN
 
281
        conn.putrequest("GET", "/bigfile", skip_host=True)
 
282
        conn.putheader("Host", self.HOST)
 
283
        conn.endheaders()
 
284
        response = conn.response_class(conn.sock, method="GET")
 
285
        response.begin()
 
286
        self.assertEqual(response.status, 200)
 
287
        body = response.fp.read(65536)
 
288
        if body != ntob("x" * len(body)):
 
289
            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
 
290
                      (65536, body[:50], len(body)))
 
291
        response.close()
 
292
        conn.close()
 
293
        
 
294
        # Make a second request, which should fetch the whole file.
 
295
        self.persistent = False
 
296
        self.getPage("/bigfile")
 
297
        if self.body != ntob("x" * BIGFILE_SIZE):
 
298
            self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
 
299
                      (BIGFILE_SIZE, self.body[:50], len(body)))
 
300