~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web2/test/test_http_headers.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from twisted.trial import unittest
 
2
import random, time
 
3
 
 
4
from twisted.web2 import http_headers
 
5
from twisted.web2.http_headers import Cookie, HeaderHandler
 
6
from twisted.python import util
 
7
 
 
8
class parsedvalue:
 
9
    """Marker class"""
 
10
    def __init__(self, raw):
 
11
        self.raw = raw
 
12
 
 
13
    def __eq__(self, other):
 
14
        return isinstance(other, parsedvalue) and other.raw == self.raw
 
15
 
 
16
class HeadersAPITest(unittest.TestCase):
 
17
    """Make sure the public API exists and works."""
 
18
    def testRaw(self):
 
19
        rawvalue = ("value1", "value2")
 
20
        h = http_headers.Headers(handler=HeaderHandler(parsers={}, generators={}))
 
21
        h.setRawHeaders("test", rawvalue)
 
22
        self.assertEquals(h.hasHeader("test"), True)
 
23
        self.assertEquals(h.getRawHeaders("test"), rawvalue)
 
24
        self.assertEquals(list(h.getAllRawHeaders()), [('Test', rawvalue)])
 
25
 
 
26
        self.assertEquals(h.getRawHeaders("foobar"), None)
 
27
        h.removeHeader("test")
 
28
        self.assertEquals(h.getRawHeaders("test"), None)
 
29
 
 
30
    def testParsed(self):
 
31
        parsed = parsedvalue(("value1", "value2"))
 
32
        h = http_headers.Headers(handler=HeaderHandler(parsers={}, generators={}))
 
33
 
 
34
        h.setHeader("test", parsed)
 
35
        self.assertEquals(h.hasHeader("test"), True)
 
36
        self.assertEquals(h.getHeader("test"), parsed)
 
37
 
 
38
        self.assertEquals(h.getHeader("foobar"), None)
 
39
        h.removeHeader("test")
 
40
        self.assertEquals(h.getHeader("test"), None)
 
41
 
 
42
    def testParsedAndRaw(self):
 
43
        def parse(raw):
 
44
            return parsedvalue(raw)
 
45
 
 
46
        def generate(parsed):
 
47
            return parsed.raw
 
48
 
 
49
        rawvalue = ("value1", "value2")
 
50
        rawvalue2 = ("value3", "value4")
 
51
        handler = HeaderHandler(parsers={'test':(parse,)},
 
52
                                generators={'test':(generate,)})
 
53
 
 
54
        h = http_headers.Headers(handler=handler)
 
55
        h.setRawHeaders("test", rawvalue)
 
56
        self.assertEquals(h.getHeader("test"), parsedvalue(rawvalue))
 
57
 
 
58
        h.setHeader("test", parsedvalue(rawvalue2))
 
59
        self.assertEquals(h.getRawHeaders("test"), rawvalue2)
 
60
 
 
61
        # Check the initializers
 
62
        h = http_headers.Headers(rawHeaders={"test": rawvalue},
 
63
                                 handler=handler)
 
64
        self.assertEquals(h.getHeader("test"), parsedvalue(rawvalue))
 
65
 
 
66
        h = http_headers.Headers({"test": parsedvalue(rawvalue2)},
 
67
                                 handler=handler)
 
68
        self.assertEquals(h.getRawHeaders("test"), rawvalue2)
 
69
 
 
70
    def testImmutable(self):
 
71
        h = http_headers.Headers(handler=HeaderHandler(parsers={}, generators={}))
 
72
 
 
73
        h.makeImmutable()
 
74
        self.assertRaises(AttributeError, h.setRawHeaders, "test", [1])
 
75
        self.assertRaises(AttributeError, h.setHeader, "test", 1)
 
76
        self.assertRaises(AttributeError, h.removeHeader, "test")
 
77
 
 
78
class TokenizerTest(unittest.TestCase):
 
79
    """Test header list parsing functions."""
 
80
 
 
81
    def testParse(self):
 
82
        parser = lambda val: list(http_headers.tokenize([val,]))
 
83
        Token = http_headers.Token
 
84
        tests = (('foo,bar', ['foo', Token(','), 'bar']),
 
85
                 ('FOO,BAR', ['foo', Token(','), 'bar']),
 
86
                 (' \t foo  \t bar  \t  ,  \t baz   ', ['foo', Token(' '), 'bar', Token(','), 'baz']),
 
87
                 ('()<>@,;:\\/[]?={}', [Token('('), Token(')'), Token('<'), Token('>'), Token('@'), Token(','), Token(';'), Token(':'), Token('\\'), Token('/'), Token('['), Token(']'), Token('?'), Token('='), Token('{'), Token('}')]),
 
88
                 (' "foo" ', ['foo']),
 
89
                 ('"FOO(),\\"BAR,"', ['FOO(),"BAR,']))
 
90
 
 
91
        raiseTests = ('"open quote', '"ending \\', "control character: \x127", "\x00", "\x1f")
 
92
 
 
93
        for test,result in tests:
 
94
            self.assertEquals(parser(test), result)
 
95
        for test in raiseTests:
 
96
            self.assertRaises(ValueError, parser, test)
 
97
 
 
98
    def testGenerate(self):
 
99
        pass
 
100
 
 
101
    def testRoundtrip(self):
 
102
        pass
 
103
 
 
104
def atSpecifiedTime(when, func):
 
105
    def inner(*a, **kw):
 
106
        orig = time.time
 
107
        time.time = lambda: when
 
108
        try:
 
109
            return func(*a, **kw)
 
110
        finally:
 
111
            time.time = orig
 
112
    return util.mergeFunctionMetadata(func, inner)
 
113
 
 
114
def parseHeader(name, val):
 
115
    head = http_headers.Headers(handler=http_headers.DefaultHTTPHandler)
 
116
    head.setRawHeaders(name,val)
 
117
    return head.getHeader(name)
 
118
parseHeader = atSpecifiedTime(999999990, parseHeader) # Sun, 09 Sep 2001 01:46:30 GMT
 
119
 
 
120
def generateHeader(name, val):
 
121
    head = http_headers.Headers(handler=http_headers.DefaultHTTPHandler)
 
122
    head.setHeader(name, val)
 
123
    return head.getRawHeaders(name)
 
124
generateHeader = atSpecifiedTime(999999990, generateHeader) # Sun, 09 Sep 2001 01:46:30 GMT
 
125
 
 
126
 
 
127
class HeaderParsingTestBase(unittest.TestCase):
 
128
    def runRoundtripTest(self, headername, table):
 
129
        """
 
130
        Perform some assertions about the behavior of parsing and
 
131
        generating HTTP headers.  Specifically: parse an HTTP header
 
132
        value, assert that the parsed form contains all the available
 
133
        information with the correct structure; generate the HTTP
 
134
        header value from the parsed form, assert that it contains
 
135
        certain literal strings; finally, re-parse the generated HTTP
 
136
        header value and assert that the resulting structured data is
 
137
        the same as the first-pass parsed form.
 
138
 
 
139
        @type headername: C{str}
 
140
        @param headername: The name of the HTTP header L{table} contains values for.
 
141
 
 
142
        @type table: A sequence of tuples describing inputs to and
 
143
        outputs from header parsing and generation.  The tuples may be
 
144
        either 2 or 3 elements long.  In either case: the first
 
145
        element is a string representing an HTTP-format header value;
 
146
        the second element is a dictionary mapping names of parameters
 
147
        to values of those parameters (the parsed form of the header).
 
148
        If there is a third element, it is a list of strings which
 
149
        must occur exactly in the HTTP header value
 
150
        string which is re-generated from the parsed form.
 
151
        """
 
152
        for row in table:
 
153
            if len(row) == 2:
 
154
                rawHeaderInput, parsedHeaderData = row
 
155
                requiredGeneratedElements = []
 
156
            elif len(row) == 3:
 
157
                rawHeaderInput, parsedHeaderData, requiredGeneratedElements = row
 
158
 
 
159
 
 
160
            assert isinstance(requiredGeneratedElements, list)
 
161
 
 
162
            # parser
 
163
            parsed = parseHeader(headername, [rawHeaderInput,])
 
164
            self.assertEquals(parsed, parsedHeaderData)
 
165
 
 
166
            regeneratedHeaderValue = generateHeader(headername, parsed)
 
167
 
 
168
            if requiredGeneratedElements:
 
169
                # generator
 
170
                for regeneratedElement in regeneratedHeaderValue:
 
171
                    reqEle = requiredGeneratedElements[regeneratedHeaderValue.index(regeneratedElement)]
 
172
                    elementIndex = regeneratedElement.find(reqEle)
 
173
                    self.assertNotEqual(
 
174
                        elementIndex, -1,
 
175
                        "%r did not appear in generated HTTP header %r: %r" % (reqEle,
 
176
                                                                               headername,
 
177
                                                                               regeneratedElement))
 
178
 
 
179
            # parser/generator
 
180
            reparsed = parseHeader(headername, regeneratedHeaderValue)
 
181
            self.assertEquals(parsed, reparsed)
 
182
 
 
183
 
 
184
    def invalidParseTest(self, headername, values):
 
185
        for val in values:
 
186
            parsed = parseHeader(headername, val)
 
187
            self.assertEquals(parsed, None)
 
188
 
 
189
class GeneralHeaderParsingTests(HeaderParsingTestBase):
 
190
    def testCacheControl(self):
 
191
        table = (
 
192
            ("no-cache",
 
193
             {'no-cache':None}),
 
194
            ("no-cache, no-store, max-age=5, max-stale=3, min-fresh=5, no-transform, only-if-cached, blahblah-extension-thingy",
 
195
             {'no-cache': None,
 
196
              'no-store': None,
 
197
              'max-age':5,
 
198
              'max-stale':3,
 
199
              'min-fresh':5,
 
200
              'no-transform':None,
 
201
              'only-if-cached':None,
 
202
              'blahblah-extension-thingy':None}),
 
203
            ("max-stale",
 
204
             {'max-stale':None}),
 
205
            ("public, private, no-cache, no-store, no-transform, must-revalidate, proxy-revalidate, max-age=5, s-maxage=10, blahblah-extension-thingy",
 
206
             {'public':None,
 
207
              'private':None,
 
208
              'no-cache':None,
 
209
              'no-store':None,
 
210
              'no-transform':None,
 
211
              'must-revalidate':None,
 
212
              'proxy-revalidate':None,
 
213
              'max-age':5,
 
214
              's-maxage':10,
 
215
              'blahblah-extension-thingy':None}),
 
216
            ('private="Set-Cookie, Set-Cookie2", no-cache="PROXY-AUTHENTICATE"',
 
217
             {'private': ['set-cookie', 'set-cookie2'],
 
218
              'no-cache': ['proxy-authenticate']},
 
219
             ['private="Set-Cookie, Set-Cookie2"', 'no-cache="Proxy-Authenticate"']),
 
220
            )
 
221
        self.runRoundtripTest("Cache-Control", table)
 
222
 
 
223
    def testConnection(self):
 
224
        table = (
 
225
            ("close", ['close',]),
 
226
            ("close, foo-bar", ['close', 'foo-bar'])
 
227
            )
 
228
        self.runRoundtripTest("Connection", table)
 
229
 
 
230
    def testDate(self):
 
231
        # Don't need major tests since the datetime parser has its own tests
 
232
        self.runRoundtripTest("Date", (("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),))
 
233
 
 
234
#     def testPragma(self):
 
235
#         fail
 
236
 
 
237
#     def testTrailer(self):
 
238
#         fail
 
239
 
 
240
    def testTransferEncoding(self):
 
241
        table = (
 
242
            ('chunked', ['chunked']),
 
243
            ('gzip, chunked', ['gzip', 'chunked'])
 
244
            )
 
245
        self.runRoundtripTest("Transfer-Encoding", table)
 
246
 
 
247
#     def testUpgrade(self):
 
248
#         fail
 
249
 
 
250
#     def testVia(self):
 
251
#         fail
 
252
 
 
253
#     def testWarning(self):
 
254
#         fail
 
255
 
 
256
class RequestHeaderParsingTests(HeaderParsingTestBase):
 
257
    #FIXME test ordering too.
 
258
    def testAccept(self):
 
259
        table = (
 
260
            ("audio/*;q=0.2, audio/basic",
 
261
             {http_headers.MimeType('audio', '*'): 0.2,
 
262
              http_headers.MimeType('audio', 'basic'): 1.0}),
 
263
 
 
264
            ("text/plain;q=0.5, text/html, text/x-dvi;q=0.8, text/x-c",
 
265
             {http_headers.MimeType('text', 'plain'): 0.5,
 
266
              http_headers.MimeType('text', 'html'): 1.0,
 
267
              http_headers.MimeType('text', 'x-dvi'): 0.8,
 
268
              http_headers.MimeType('text', 'x-c'): 1.0}),
 
269
 
 
270
            ("text/*, text/html, text/html;level=1, */*",
 
271
             {http_headers.MimeType('text', '*'): 1.0,
 
272
              http_headers.MimeType('text', 'html'): 1.0,
 
273
              http_headers.MimeType('text', 'html', (('level', '1'),)): 1.0,
 
274
              http_headers.MimeType('*', '*'): 1.0}),
 
275
 
 
276
       ("text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
 
277
             {http_headers.MimeType('text', '*'): 0.3,
 
278
              http_headers.MimeType('text', 'html'): 0.7,
 
279
              http_headers.MimeType('text', 'html', (('level', '1'),)): 1.0,
 
280
              http_headers.MimeType('text', 'html', (('level', '2'),)): 0.4,
 
281
              http_headers.MimeType('*', '*'): 0.5}),
 
282
            )
 
283
 
 
284
        self.runRoundtripTest("Accept", table)
 
285
 
 
286
 
 
287
    def testAcceptCharset(self):
 
288
        table = (
 
289
            ("iso-8859-5, unicode-1-1;q=0.8",
 
290
             {'iso-8859-5': 1.0, 'iso-8859-1': 1.0, 'unicode-1-1': 0.8},
 
291
             ["iso-8859-5", "unicode-1-1;q=0.8", "iso-8859-1"]),
 
292
            ("iso-8859-1;q=0.7",
 
293
             {'iso-8859-1': 0.7}),
 
294
            ("*;q=.7",
 
295
             {'*': 0.7},
 
296
             ["*;q=0.7"]),
 
297
            ("",
 
298
             {'iso-8859-1': 1.0},
 
299
             ["iso-8859-1"]), # Yes this is an actual change -- we'll say that's okay. :)
 
300
            )
 
301
        self.runRoundtripTest("Accept-Charset", table)
 
302
 
 
303
    def testAcceptEncoding(self):
 
304
        table = (
 
305
            ("compress, gzip",
 
306
             {'compress': 1.0, 'gzip': 1.0, 'identity': 0.0001}),
 
307
            ("",
 
308
             {'identity': 0.0001}),
 
309
            ("*",
 
310
             {'*': 1}),
 
311
            ("compress;q=0.5, gzip;q=1.0",
 
312
             {'compress': 0.5, 'gzip': 1.0, 'identity': 0.0001},
 
313
             ["compress;q=0.5", "gzip"]),
 
314
            ("gzip;q=1.0, identity;q=0.5, *;q=0",
 
315
             {'gzip': 1.0, 'identity': 0.5, '*':0},
 
316
             ["gzip", "identity;q=0.5", "*;q=0"]),
 
317
            )
 
318
        self.runRoundtripTest("Accept-Encoding", table)
 
319
 
 
320
    def testAcceptLanguage(self):
 
321
        table = (
 
322
            ("da, en-gb;q=0.8, en;q=0.7",
 
323
             {'da': 1.0, 'en-gb': 0.8, 'en': 0.7}),
 
324
            ("*",
 
325
             {'*': 1}),
 
326
            )
 
327
        self.runRoundtripTest("Accept-Language", table)
 
328
 
 
329
    def testAuthorization(self):
 
330
        table = (
 
331
            ("Basic dXNlcm5hbWU6cGFzc3dvcmQ=",
 
332
             ("basic", "dXNlcm5hbWU6cGFzc3dvcmQ="),
 
333
             ["basic dXNlcm5hbWU6cGFzc3dvcmQ="]),
 
334
            ('Digest nonce="bar", realm="foo", username="baz", response="bax"',
 
335
             ('digest', 'nonce="bar", realm="foo", username="baz", response="bax"'),
 
336
             ['digest', 'nonce="bar"', 'realm="foo"', 'username="baz"', 'response="bax"'])
 
337
            )
 
338
 
 
339
        self.runRoundtripTest("Authorization", table)
 
340
 
 
341
    def testCookie(self):
 
342
        table = (
 
343
            ('name=value', [Cookie('name', 'value')]),
 
344
            ('"name"="value"', [Cookie('"name"', '"value"')]),
 
345
            ('name,"blah=value,"', [Cookie('name,"blah', 'value,"')]),
 
346
            ('name,"blah  = value,"  ', [Cookie('name,"blah', 'value,"')], ['name,"blah=value,"']),
 
347
            ("`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?=`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?", [Cookie("`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?", "`~!@#$%^&*()-_+[{]}\\|:'\",<.>/?")]),
 
348
            ('name,"blah  = value,"  ; name2=val2',
 
349
               [Cookie('name,"blah', 'value,"'), Cookie('name2', 'val2')],
 
350
               ['name,"blah=value,"', 'name2=val2']),
 
351
            )
 
352
        self.runRoundtripTest("Cookie", table)
 
353
 
 
354
        #newstyle RFC2965 Cookie
 
355
        table2 = (
 
356
            ('$Version="1";'
 
357
             'name="value";$Path="/foo";$Domain="www.local";$Port="80,8000";'
 
358
             'name2="value"',
 
359
             [Cookie('name', 'value', path='/foo', domain='www.local', ports=(80,8000), version=1), Cookie('name2', 'value', version=1)]),
 
360
            ('$Version="1";'
 
361
             'name="value";$Port',
 
362
             [Cookie('name', 'value', ports=(), version=1)]),
 
363
            ('$Version = 1, NAME = "qq\\"qq",Frob=boo',
 
364
             [Cookie('name', 'qq"qq', version=1), Cookie('frob', 'boo', version=1)],
 
365
             ['$Version="1";name="qq\\"qq";frob="boo"']),
 
366
            )
 
367
        self.runRoundtripTest("Cookie", table2)
 
368
 
 
369
        # Generate only!
 
370
        # make headers by combining oldstyle and newstyle cookies
 
371
        table3 = (
 
372
            ([Cookie('name', 'value'), Cookie('name2', 'value2', version=1)],
 
373
             '$Version="1";name=value;name2="value2"'),
 
374
            ([Cookie('name', 'value', path="/foo"), Cookie('name2', 'value2', domain="bar.baz", version=1)],
 
375
             '$Version="1";name=value;$Path="/foo";name2="value2";$Domain="bar.baz"'),
 
376
            ([Cookie('invalid,"name', 'value'), Cookie('name2', 'value2', version=1)],
 
377
             '$Version="1";name2="value2"'),
 
378
            ([Cookie('name', 'qq"qq'), Cookie('name2', 'value2', version=1)],
 
379
             '$Version="1";name="qq\\"qq";name2="value2"'),
 
380
            )
 
381
        for row in table3:
 
382
            self.assertEquals(generateHeader("Cookie", row[0]), [row[1],])
 
383
 
 
384
 
 
385
 
 
386
    def testSetCookie(self):
 
387
        table = (
 
388
            ('name,"blah=value,; expires=Sun, 09 Sep 2001 01:46:40 GMT; path=/foo; domain=bar.baz; secure',
 
389
             [Cookie('name,"blah', 'value,', expires=1000000000, path="/foo", domain="bar.baz", secure=True)]),
 
390
            ('name,"blah = value, ; expires="Sun, 09 Sep 2001 01:46:40 GMT"',
 
391
             [Cookie('name,"blah', 'value,', expires=1000000000)],
 
392
             ['name,"blah=value,', 'expires=Sun, 09 Sep 2001 01:46:40 GMT']),
 
393
            )
 
394
        self.runRoundtripTest("Set-Cookie", table)
 
395
 
 
396
    def testSetCookie2(self):
 
397
        table = (
 
398
            ('name="value"; Comment="YadaYada"; CommentURL="http://frobnotz/"; Discard; Domain="blah.blah"; Max-Age=10; Path="/foo"; Port="80,8080"; Secure; Version="1"',
 
399
             [Cookie("name", "value", comment="YadaYada", commenturl="http://frobnotz/", discard=True, domain="blah.blah", expires=1000000000, path="/foo", ports=(80,8080), secure=True, version=1)]),
 
400
            )
 
401
        self.runRoundtripTest("Set-Cookie2", table)
 
402
 
 
403
    def testExpect(self):
 
404
        table = (
 
405
            ("100-continue",
 
406
             {"100-continue":(None,)}),
 
407
            ('foobar=twiddle',
 
408
             {'foobar':('twiddle',)}),
 
409
            ("foo=bar;a=b;c",
 
410
             {'foo':('bar',('a', 'b'), ('c', None))})
 
411
            )
 
412
        self.runRoundtripTest("Expect", table)
 
413
 
 
414
    def testFrom(self):
 
415
        self.runRoundtripTest("From", (("webmaster@w3.org", "webmaster@w3.org"),))
 
416
 
 
417
    def testHost(self):
 
418
        self.runRoundtripTest("Host", (("www.w3.org", "www.w3.org"),))
 
419
 
 
420
    def testIfMatch(self):
 
421
        table = (
 
422
            ('"xyzzy"', [http_headers.ETag('xyzzy')]),
 
423
            ('"xyzzy", "r2d2xxxx", "c3piozzzz"', [http_headers.ETag('xyzzy'),
 
424
                                                    http_headers.ETag('r2d2xxxx'),
 
425
                                                    http_headers.ETag('c3piozzzz')]),
 
426
            ('*', ['*']),
 
427
            )
 
428
    def testIfModifiedSince(self):
 
429
        # Don't need major tests since the datetime parser has its own test
 
430
        # Just test stupid ; length= brokenness.
 
431
        table = (
 
432
            ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),
 
433
            ("Sun, 09 Sep 2001 01:46:40 GMT; length=500", 1000000000, ["Sun, 09 Sep 2001 01:46:40 GMT"]),
 
434
            )
 
435
 
 
436
        self.runRoundtripTest("If-Modified-Since", table)
 
437
 
 
438
    def testIfNoneMatch(self):
 
439
        table = (
 
440
            ('"xyzzy"', [http_headers.ETag('xyzzy')]),
 
441
            ('W/"xyzzy", "r2d2xxxx", "c3piozzzz"', [http_headers.ETag('xyzzy', weak=True),
 
442
                                                    http_headers.ETag('r2d2xxxx'),
 
443
                                                    http_headers.ETag('c3piozzzz')]),
 
444
            ('W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"', [http_headers.ETag('xyzzy', weak=True),
 
445
                                                        http_headers.ETag('r2d2xxxx', weak=True),
 
446
                                                        http_headers.ETag('c3piozzzz', weak=True)]),
 
447
            ('*', ['*']),
 
448
            )
 
449
        self.runRoundtripTest("If-None-Match", table)
 
450
 
 
451
    def testIfRange(self):
 
452
        table = (
 
453
            ('"xyzzy"', http_headers.ETag('xyzzy')),
 
454
            ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
 
455
            ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
 
456
            ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),
 
457
            )
 
458
        self.runRoundtripTest("If-Range", table)
 
459
 
 
460
    def testIfUnmodifiedSince(self):
 
461
        self.runRoundtripTest("If-Unmodified-Since", (("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),))
 
462
 
 
463
    def testMaxForwards(self):
 
464
        self.runRoundtripTest("Max-Forwards", (("15", 15),))
 
465
 
 
466
 
 
467
#     def testProxyAuthorize(self):
 
468
#         fail
 
469
 
 
470
    def testRange(self):
 
471
        table = (
 
472
            ("bytes=0-499", ('bytes', [(0,499),])),
 
473
            ("bytes=500-999", ('bytes', [(500,999),])),
 
474
            ("bytes=-500",('bytes', [(None,500),])),
 
475
            ("bytes=9500-",('bytes', [(9500, None),])),
 
476
            ("bytes=0-0,-1", ('bytes', [(0,0),(None,1)])),
 
477
            )
 
478
        self.runRoundtripTest("Range", table)
 
479
 
 
480
 
 
481
    def testReferer(self):
 
482
        self.runRoundtripTest("Referer", (("http://www.w3.org/hypertext/DataSources/Overview.html",
 
483
                                           "http://www.w3.org/hypertext/DataSources/Overview.html"),))
 
484
 
 
485
 
 
486
    def testTE(self):
 
487
        table = (
 
488
            ("deflate", {'deflate':1}),
 
489
            ("", {}),
 
490
            ("trailers, deflate;q=0.5", {'trailers':1, 'deflate':0.5}),
 
491
            )
 
492
        self.runRoundtripTest("TE", table)
 
493
 
 
494
    def testUserAgent(self):
 
495
        self.runRoundtripTest("User-Agent", (("CERN-LineMode/2.15 libwww/2.17b3", "CERN-LineMode/2.15 libwww/2.17b3"),))
 
496
 
 
497
 
 
498
class ResponseHeaderParsingTests(HeaderParsingTestBase):
 
499
    def testAcceptRanges(self):
 
500
        self.runRoundtripTest("Accept-Ranges", (("bytes", ["bytes"]), ("none", ["none"])))
 
501
 
 
502
    def testAge(self):
 
503
        self.runRoundtripTest("Age", (("15", 15),))
 
504
 
 
505
    def testETag(self):
 
506
        table = (
 
507
            ('"xyzzy"', http_headers.ETag('xyzzy')),
 
508
            ('W/"xyzzy"', http_headers.ETag('xyzzy', weak=True)),
 
509
            ('""', http_headers.ETag('')),
 
510
            )
 
511
        self.runRoundtripTest("ETag", table)
 
512
 
 
513
    def testLocation(self):
 
514
        self.runRoundtripTest("Location", (("http://www.w3.org/pub/WWW/People.htm",
 
515
                                           "http://www.w3.org/pub/WWW/People.htm"),))
 
516
 
 
517
 
 
518
#     def testProxyAuthenticate(self):
 
519
#         fail
 
520
 
 
521
    def testRetryAfter(self):
 
522
        # time() is always 999999990 when being tested.
 
523
        table = (
 
524
            ("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000, ["10"]),
 
525
            ("120", 999999990+120),
 
526
            )
 
527
        self.runRoundtripTest("Retry-After", table)
 
528
 
 
529
    def testServer(self):
 
530
        self.runRoundtripTest("Server", (("CERN/3.0 libwww/2.17", "CERN/3.0 libwww/2.17"),))
 
531
 
 
532
    def testVary(self):
 
533
        table = (
 
534
            ("*", ["*"]),
 
535
            ("Accept, Accept-Encoding", ["accept", "accept-encoding"], ["accept", "accept-encoding"])
 
536
            )
 
537
        self.runRoundtripTest("Vary", table)
 
538
 
 
539
    def testWWWAuthenticate(self):
 
540
        digest = ('Digest realm="digest realm", nonce="bAr", qop="auth"',
 
541
                  [('Digest', {'realm': 'digest realm', 'nonce': 'bAr', 
 
542
                               'qop': 'auth'})],
 
543
                  ['Digest', 'realm="digest realm"', 
 
544
                   'nonce="bAr"', 'qop="auth"'])
 
545
 
 
546
        basic = ('Basic realm="foo"',
 
547
                 [('Basic', {'realm': 'foo'})], ['Basic', 'realm="foo"'])
 
548
 
 
549
        ntlm = ('NTLM',
 
550
                [('NTLM', {})], ['NTLM', ''])
 
551
 
 
552
        negotiate = ('Negotiate SomeGssAPIData',
 
553
                     [('Negotiate', 'SomeGssAPIData')], 
 
554
                     ['Negotiate', 'SomeGssAPIData'])
 
555
 
 
556
        table = (digest,
 
557
                 basic,
 
558
                 (digest[0]+', '+basic[0],
 
559
                  digest[1] + basic[1],
 
560
                  [digest[2], basic[2]]),
 
561
                 ntlm,
 
562
                 negotiate,
 
563
                 (ntlm[0]+', '+basic[0],
 
564
                  ntlm[1] + basic[1],
 
565
                  [ntlm[2], basic[2]]),
 
566
                 (digest[0]+', '+negotiate[0],
 
567
                  digest[1] + negotiate[1],
 
568
                  [digest[2], negotiate[2]]),
 
569
                 (negotiate[0]+', '+negotiate[0],
 
570
                  negotiate[1] + negotiate[1],
 
571
                  [negotiate[2] + negotiate[2]]),
 
572
                 (ntlm[0]+', '+ntlm[0],
 
573
                  ntlm[1] + ntlm[1],
 
574
                  [ntlm[2], ntlm[2]]),
 
575
                 (basic[0]+', '+ntlm[0],
 
576
                  basic[1] + ntlm[1],
 
577
                  [basic[2], ntlm[2]]),
 
578
                 )
 
579
 
 
580
        # runRoundtripTest doesn't work because we don't generate a single
 
581
        # header
 
582
 
 
583
        headername = 'WWW-Authenticate'
 
584
 
 
585
        for row in table:
 
586
            rawHeaderInput, parsedHeaderData, requiredGeneratedElements = row
 
587
 
 
588
            parsed = parseHeader(headername, [rawHeaderInput,])
 
589
            self.assertEquals(parsed, parsedHeaderData)
 
590
 
 
591
            regeneratedHeaderValue = generateHeader(headername, parsed)
 
592
 
 
593
            for regeneratedElement in regeneratedHeaderValue:
 
594
                requiredElements = requiredGeneratedElements[
 
595
                    regeneratedHeaderValue.index(
 
596
                        regeneratedElement)]
 
597
 
 
598
                for reqEle in requiredElements:
 
599
                    elementIndex = regeneratedElement.find(reqEle)
 
600
 
 
601
                    self.assertNotEqual(
 
602
                        elementIndex, -1,
 
603
                        "%r did not appear in generated HTTP header %r: %r" % (reqEle,
 
604
                                                                               headername,
 
605
                                                                               regeneratedElement))
 
606
 
 
607
        # parser/generator
 
608
        reparsed = parseHeader(headername, regeneratedHeaderValue)
 
609
        self.assertEquals(parsed, reparsed)
 
610
 
 
611
 
 
612
class EntityHeaderParsingTests(HeaderParsingTestBase):
 
613
    def testAllow(self):
 
614
        # Allow is a silly case-sensitive header unlike all the rest
 
615
        table = (
 
616
            ("GET", ['GET', ]),
 
617
            ("GET, HEAD, PUT", ['GET', 'HEAD', 'PUT']),
 
618
            )
 
619
        self.runRoundtripTest("Allow", table)
 
620
 
 
621
    def testContentEncoding(self):
 
622
        table = (
 
623
            ("gzip", ['gzip',]),
 
624
            )
 
625
        self.runRoundtripTest("Content-Encoding", table)
 
626
 
 
627
    def testContentLanguage(self):
 
628
        table = (
 
629
            ("da", ['da',]),
 
630
            ("mi, en", ['mi', 'en']),
 
631
            )
 
632
        self.runRoundtripTest("Content-Language", table)
 
633
 
 
634
    def testContentLength(self):
 
635
        self.runRoundtripTest("Content-Length", (("15", 15),))
 
636
        self.invalidParseTest("Content-Length", ("asdf",))
 
637
 
 
638
    def testContentLocation(self):
 
639
        self.runRoundtripTest("Content-Location",
 
640
                              (("http://www.w3.org/pub/WWW/People.htm",
 
641
                                "http://www.w3.org/pub/WWW/People.htm"),))
 
642
 
 
643
    def testContentMD5(self):
 
644
        self.runRoundtripTest("Content-MD5", (("Q2hlY2sgSW50ZWdyaXR5IQ==", "Check Integrity!"),))
 
645
        self.invalidParseTest("Content-MD5", ("sdlaksjdfhlkaj",))
 
646
 
 
647
    def testContentRange(self):
 
648
        table = (
 
649
            ("bytes 0-499/1234", ("bytes", 0, 499, 1234)),
 
650
            ("bytes 500-999/1234", ("bytes", 500, 999, 1234)),
 
651
            ("bytes 500-1233/1234", ("bytes", 500, 1233, 1234)),
 
652
            ("bytes 734-1233/1234", ("bytes", 734, 1233, 1234)),
 
653
            ("bytes 734-1233/*", ("bytes", 734, 1233, None)),
 
654
            ("bytes */1234", ("bytes", None, None, 1234)),
 
655
            ("bytes */*", ("bytes", None, None, None))
 
656
            )
 
657
        self.runRoundtripTest("Content-Range", table)
 
658
 
 
659
    def testContentType(self):
 
660
        table = (
 
661
            ("text/html;charset=iso-8859-4", http_headers.MimeType('text', 'html', (('charset','iso-8859-4'),))),
 
662
            ("text/html", http_headers.MimeType('text', 'html')),
 
663
            )
 
664
        self.runRoundtripTest("Content-Type", table)
 
665
 
 
666
    def testExpires(self):
 
667
        self.runRoundtripTest("Expires", (("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),))
 
668
        # Invalid expires MUST return date in the past.
 
669
        self.assertEquals(parseHeader("Expires", ["0"]), 0)
 
670
        self.assertEquals(parseHeader("Expires", ["wejthnaljn"]), 0)
 
671
 
 
672
 
 
673
    def testLastModified(self):
 
674
        # Don't need major tests since the datetime parser has its own test
 
675
        self.runRoundtripTest("Last-Modified", (("Sun, 09 Sep 2001 01:46:40 GMT", 1000000000),))
 
676
 
 
677
class DateTimeTest(unittest.TestCase):
 
678
    """Test date parsing functions."""
 
679
 
 
680
    def testParse(self):
 
681
        timeNum = 784111777
 
682
        timeStrs = ('Sun, 06 Nov 1994 08:49:37 GMT',
 
683
                    'Sunday, 06-Nov-94 08:49:37 GMT',
 
684
                    'Sun Nov  6 08:49:37 1994',
 
685
 
 
686
                    # Also some non-RFC formats, for good measure.
 
687
                    'Somefakeday 6 Nov 1994 8:49:37',
 
688
                    '6 Nov 1994 8:49:37',
 
689
                    'Sun, 6 Nov 1994 8:49:37',
 
690
                    '6 Nov 1994 8:49:37 GMT',
 
691
 
 
692
                    '06-Nov-94 08:49:37',
 
693
                    'Sunday, 06-Nov-94 08:49:37',
 
694
                    '06-Nov-94 08:49:37 GMT',
 
695
 
 
696
                    'Nov  6 08:49:37 1994',
 
697
                    )
 
698
        for timeStr in timeStrs:
 
699
            self.assertEquals(http_headers.parseDateTime(timeStr), timeNum)
 
700
 
 
701
        # Test 2 Digit date wraparound yuckiness.
 
702
        self.assertEquals(http_headers.parseDateTime(
 
703
            'Monday, 11-Oct-04 14:56:50 GMT'), 1097506610)
 
704
        self.assertEquals(http_headers.parseDateTime(
 
705
            'Monday, 11-Oct-2004 14:56:50 GMT'), 1097506610)
 
706
 
 
707
 
 
708
    def testGenerate(self):
 
709
        self.assertEquals(http_headers.generateDateTime(784111777), 'Sun, 06 Nov 1994 08:49:37 GMT')
 
710
 
 
711
    def testRoundtrip(self):
 
712
        for i in range(2000):
 
713
            time = random.randint(0, 2000000000)
 
714
            timestr = http_headers.generateDateTime(time)
 
715
            time2 = http_headers.parseDateTime(timestr)
 
716
            self.assertEquals(time, time2)
 
717
 
 
718
 
 
719
class TestMimeType(unittest.TestCase):
 
720
    def testEquality(self):
 
721
        """Test that various uses of the constructer are equal
 
722
        """
 
723
 
 
724
        kwargMime = http_headers.MimeType('text', 'plain',
 
725
                                          key='value',
 
726
                                          param=None)
 
727
        dictMime = http_headers.MimeType('text', 'plain',
 
728
                                         {'param': None,
 
729
                                          'key': 'value'})
 
730
        tupleMime = http_headers.MimeType('text', 'plain',
 
731
                                          (('param', None),
 
732
                                           ('key', 'value')))
 
733
 
 
734
        stringMime = http_headers.MimeType.fromString('text/plain;key=value;param')
 
735
 
 
736
        self.assertEquals(kwargMime, dictMime)
 
737
        self.assertEquals(dictMime, tupleMime)
 
738
        self.assertEquals(kwargMime, tupleMime)
 
739
        self.assertEquals(kwargMime, stringMime)