summaryrefslogtreecommitdiff
path: root/test/test_sendfile.py
blob: ef8eef582ca350b18cbe44d0f7afaf5199c9c761 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import unittest
from bottle import static_file, request, response, parse_date, parse_range_header, Bottle, tob
import bottle
import wsgiref.util
import os
import tempfile
import time

basename = os.path.basename(__file__)
root = os.path.dirname(__file__)

basename2 = os.path.basename(bottle.__file__)
root2 = os.path.dirname(bottle.__file__)


weekday_full = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
weekday_abbr = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
month_abbr = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

class TestDateParser(unittest.TestCase):
    def test_rfc1123(self):
        """DateParser: RFC 1123 format"""
        ts = time.time()
        rs = bottle.http_date(ts)
        self.assertEqual(int(ts), int(parse_date(rs)))

    def test_rfc850(self):
        """DateParser: RFC 850 format"""
        ts = time.time()
        t = time.gmtime(ts)
        rs = time.strftime("%%s, %d-%%s-%y %H:%M:%S GMT", t) % (weekday_full[t.tm_wday], month_abbr[t.tm_mon])
        self.assertEqual(int(ts), int(parse_date(rs)))

    def test_asctime(self):
        """DateParser: asctime format"""
        ts = time.time()
        t = time.gmtime(ts)
        rs = time.strftime("%%s %%s %d %H:%M:%S %Y", t) % (weekday_abbr[t.tm_wday], month_abbr[t.tm_mon])
        self.assertEqual(int(ts), int(parse_date(rs)))

    def test_bad(self):
        """DateParser: Bad format"""
        self.assertEqual(None, parse_date('Bad 123'))


class TestSendFile(unittest.TestCase):
    def setUp(self):
        e = dict()
        wsgiref.util.setup_testing_defaults(e)
        b = Bottle()
        request.bind(e)
        response.bind()

    def test_valid(self):
        """ SendFile: Valid requests"""
        out = static_file(basename, root=root)
        self.assertEqual(open(__file__,'rb').read(), out.body.read())

    def test_invalid(self):
        """ SendFile: Invalid requests"""
        self.assertEqual(404, static_file('not/a/file', root=root).status_code)
        f = static_file(os.path.join('./../', basename), root='./views/')
        self.assertEqual(403, f.status_code)

    def test_file_not_readable(self):
        if os.geteuid() == 0:
            return # Root can read anything

        try:
            fp, fn = tempfile.mkstemp()
            os.chmod(fn, 0)
            self.assertEqual(403, static_file(fn, root='/').status_code)
        finally:
            os.close(fp)
            os.unlink(fn)

    def test_mime(self):
        """ SendFile: Mime Guessing"""
        f = static_file(basename, root=root)
        self.assertTrue(f.headers['Content-Type'].split(';')[0] in ('application/x-python-code', 'text/x-python'))
        f = static_file(basename, root=root, mimetype='some/type')
        self.assertEqual('some/type', f.headers['Content-Type'])
        f = static_file(basename, root=root, mimetype='text/foo')
        self.assertEqual('text/foo; charset=UTF-8', f.headers['Content-Type'])
        f = static_file(basename, root=root, mimetype='text/foo', charset='latin1')
        self.assertEqual('text/foo; charset=latin1', f.headers['Content-Type'])

    def test_ims(self):
        """ SendFile: If-Modified-Since"""
        request.environ['HTTP_IF_MODIFIED_SINCE'] = bottle.http_date(time.time())
        res = static_file(basename, root=root)
        self.assertEqual(304, res.status_code)
        self.assertEqual(int(os.stat(__file__).st_mtime), parse_date(res.headers['Last-Modified']))
        self.assertAlmostEqual(int(time.time()), parse_date(res.headers['Date']))
        request.environ['HTTP_IF_MODIFIED_SINCE'] = bottle.http_date(100)
        self.assertEqual(open(__file__,'rb').read(), static_file(basename, root=root).body.read())

    def test_ims_empty(self):
        """ SendFile: Empty If-Modified-Since"""
        request.environ['HTTP_IF_MODIFIED_SINCE'] = ''
        self.assertEqual(open(__file__, 'rb').read(), static_file(basename, root=root).body.read())

    def test_etag(self):
        """ SendFile: If-Modified-Since"""
        res = static_file(basename, root=root)
        self.assertTrue('ETag' in res.headers)
        self.assertEqual(200, res.status_code)
        etag = res.headers['ETag']
        
        request.environ['HTTP_IF_NONE_MATCH'] = etag
        res = static_file(basename, root=root)
        self.assertTrue('ETag' in res.headers)
        self.assertEqual(etag, res.headers['ETag'])
        self.assertEqual(304, res.status_code)

        request.environ['HTTP_IF_NONE_MATCH'] = etag
        res = static_file(basename2, root=root2)
        self.assertTrue('ETag' in res.headers)
        self.assertNotEqual(etag, res.headers['ETag'])
        self.assertEqual(200, res.status_code)
       

    def test_download(self):
        """ SendFile: Download as attachment """
        f = static_file(basename, root=root, download="foo.mp3")
        self.assertEqual('audio/mpeg', f.headers['Content-Type'])

        f = static_file(basename, root=root, download=True)
        self.assertEqual('attachment; filename="%s"' % basename, f.headers['Content-Disposition'])
        request.environ['HTTP_IF_MODIFIED_SINCE'] = bottle.http_date(100)

        f = static_file(basename, root=root)
        self.assertEqual(open(__file__,'rb').read(), f.body.read())

    def test_range(self):
        request.environ['HTTP_RANGE'] = 'bytes=10-25,-80'
        f = static_file(basename, root=root)
        c = open(__file__, 'rb'); c.seek(10)
        self.assertEqual(c.read(16), tob('').join(f.body))
        self.assertEqual('bytes 10-25/%d' % len(open(__file__, 'rb').read()),
                         f.headers['Content-Range'])
        self.assertEqual('bytes', f.headers['Accept-Ranges'])

    def test_range_parser(self):
        r = lambda rs: list(parse_range_header(rs, 100))
        self.assertEqual([(90, 100)], r('bytes=-10'))
        self.assertEqual([(10, 100)], r('bytes=10-'))
        self.assertEqual([(5, 11)],  r('bytes=5-10'))
        self.assertEqual([(10, 100), (90, 100), (5, 11)],  r('bytes=10-,-10,5-10'))

    def test_custom_headers(self):
        """ SendFile: Custom headers """
        headers = {'X-Custom-Header': 'test-value'}
        headers_orig = headers.copy()
        res = static_file(basename, root=root, headers=headers)
        self.assertTrue('X-Custom-Header' in res.headers)
        self.assertEqual('test-value', res.headers['X-Custom-Header'])
        # Check the passed in headers dict isn't modified.
        self.assertEqual(headers_orig, headers)