~j5-dev/+junk/cherrypy3-3.2.0rc1

« back to all changes in this revision

Viewing changes to cherrypy/test/test_conn.py

  • Committer: steveh at sjsoft
  • Date: 2010-07-01 13:07:15 UTC
  • Revision ID: steveh@sjsoft.com-20100701130715-w56oim8346qzqlka
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Tests for TCP connection handling, including proper and timely close."""
 
2
 
 
3
from cherrypy.test import test
 
4
test.prefer_parent_path()
 
5
 
 
6
from httplib import HTTPConnection, HTTPSConnection, NotConnected, BadStatusLine
 
7
import urllib
 
8
import socket
 
9
import sys
 
10
import time
 
11
timeout = 1
 
12
 
 
13
 
 
14
import cherrypy
 
15
from cherrypy.test import webtest
 
16
from cherrypy import _cperror
 
17
 
 
18
 
 
19
pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
 
20
 
 
21
def setup_server():
 
22
    
 
23
    def raise500():
 
24
        raise cherrypy.HTTPError(500)
 
25
    
 
26
    class Root:
 
27
        
 
28
        def index(self):
 
29
            return pov
 
30
        index.exposed = True
 
31
        page1 = index
 
32
        page2 = index
 
33
        page3 = index
 
34
        
 
35
        def hello(self):
 
36
            return "Hello, world!"
 
37
        hello.exposed = True
 
38
        
 
39
        def timeout(self, t):
 
40
            return str(cherrypy.server.httpserver.timeout)
 
41
        timeout.exposed = True
 
42
        
 
43
        def stream(self, set_cl=False):
 
44
            if set_cl:
 
45
                cherrypy.response.headers['Content-Length'] = 10
 
46
            
 
47
            def content():
 
48
                for x in range(10):
 
49
                    yield str(x)
 
50
            
 
51
            return content()
 
52
        stream.exposed = True
 
53
        stream._cp_config = {'response.stream': True}
 
54
        
 
55
        def error(self, code=500):
 
56
            raise cherrypy.HTTPError(code)
 
57
        error.exposed = True
 
58
        
 
59
        def upload(self):
 
60
            if not cherrypy.request.method == 'POST':
 
61
                raise AssertionError("'POST' != request.method %r" %
 
62
                                     cherrypy.request.method)
 
63
            return "thanks for '%s'" % cherrypy.request.body.read()
 
64
        upload.exposed = True
 
65
        
 
66
        def custom(self, response_code):
 
67
            cherrypy.response.status = response_code
 
68
            return "Code = %s" % response_code
 
69
        custom.exposed = True
 
70
        
 
71
        def err_before_read(self):
 
72
            return "ok"
 
73
        err_before_read.exposed = True
 
74
        err_before_read._cp_config = {'hooks.on_start_resource': raise500}
 
75
 
 
76
        def one_megabyte_of_a(self):
 
77
            return ["a" * 1024] * 1024
 
78
        one_megabyte_of_a.exposed = True
 
79
    
 
80
    cherrypy.tree.mount(Root())
 
81
    cherrypy.config.update({
 
82
        'server.max_request_body_size': 1001,
 
83
        'server.socket_timeout': timeout,
 
84
        })
 
85
 
 
86
 
 
87
from cherrypy.test import helper
 
88
 
 
89
class ConnectionCloseTests(helper.CPWebCase):
 
90
    
 
91
    def test_HTTP11(self):
 
92
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
93
            return self.skip()
 
94
        
 
95
        self.PROTOCOL = "HTTP/1.1"
 
96
        
 
97
        self.persistent = True
 
98
        
 
99
        # Make the first request and assert there's no "Connection: close".
 
100
        self.getPage("/")
 
101
        self.assertStatus('200 OK')
 
102
        self.assertBody(pov)
 
103
        self.assertNoHeader("Connection")
 
104
        
 
105
        # Make another request on the same connection.
 
106
        self.getPage("/page1")
 
107
        self.assertStatus('200 OK')
 
108
        self.assertBody(pov)
 
109
        self.assertNoHeader("Connection")
 
110
        
 
111
        # Test client-side close.
 
112
        self.getPage("/page2", headers=[("Connection", "close")])
 
113
        self.assertStatus('200 OK')
 
114
        self.assertBody(pov)
 
115
        self.assertHeader("Connection", "close")
 
116
        
 
117
        # Make another request on the same connection, which should error.
 
118
        self.assertRaises(NotConnected, self.getPage, "/")
 
119
    
 
120
    def test_Streaming_no_len(self):
 
121
        self._streaming(set_cl=False)
 
122
    
 
123
    def test_Streaming_with_len(self):
 
124
        self._streaming(set_cl=True)
 
125
    
 
126
    def _streaming(self, set_cl):
 
127
        if cherrypy.server.protocol_version == "HTTP/1.1":
 
128
            self.PROTOCOL = "HTTP/1.1"
 
129
            
 
130
            self.persistent = True
 
131
            
 
132
            # Make the first request and assert there's no "Connection: close".
 
133
            self.getPage("/")
 
134
            self.assertStatus('200 OK')
 
135
            self.assertBody(pov)
 
136
            self.assertNoHeader("Connection")
 
137
            
 
138
            # Make another, streamed request on the same connection.
 
139
            if set_cl:
 
140
                # When a Content-Length is provided, the content should stream
 
141
                # without closing the connection.
 
142
                self.getPage("/stream?set_cl=Yes")
 
143
                self.assertHeader("Content-Length")
 
144
                self.assertNoHeader("Connection", "close")
 
145
                self.assertNoHeader("Transfer-Encoding")
 
146
                
 
147
                self.assertStatus('200 OK')
 
148
                self.assertBody('0123456789')
 
149
            else:
 
150
                # When no Content-Length response header is provided,
 
151
                # streamed output will either close the connection, or use
 
152
                # chunked encoding, to determine transfer-length.
 
153
                self.getPage("/stream")
 
154
                self.assertNoHeader("Content-Length")
 
155
                self.assertStatus('200 OK')
 
156
                self.assertBody('0123456789')
 
157
                
 
158
                chunked_response = False
 
159
                for k, v in self.headers:
 
160
                    if k.lower() == "transfer-encoding":
 
161
                        if str(v) == "chunked":
 
162
                            chunked_response = True
 
163
                
 
164
                if chunked_response:
 
165
                    self.assertNoHeader("Connection", "close")
 
166
                else:
 
167
                    self.assertHeader("Connection", "close")
 
168
                    
 
169
                    # Make another request on the same connection, which should error.
 
170
                    self.assertRaises(NotConnected, self.getPage, "/")
 
171
                
 
172
                # Try HEAD. See http://www.cherrypy.org/ticket/864.
 
173
                self.getPage("/stream", method='HEAD')
 
174
                self.assertStatus('200 OK')
 
175
                self.assertBody('')
 
176
                self.assertNoHeader("Transfer-Encoding")
 
177
        else:
 
178
            self.PROTOCOL = "HTTP/1.0"
 
179
            
 
180
            self.persistent = True
 
181
            
 
182
            # Make the first request and assert Keep-Alive.
 
183
            self.getPage("/", headers=[("Connection", "Keep-Alive")])
 
184
            self.assertStatus('200 OK')
 
185
            self.assertBody(pov)
 
186
            self.assertHeader("Connection", "Keep-Alive")
 
187
            
 
188
            # Make another, streamed request on the same connection.
 
189
            if set_cl:
 
190
                # When a Content-Length is provided, the content should
 
191
                # stream without closing the connection.
 
192
                self.getPage("/stream?set_cl=Yes",
 
193
                             headers=[("Connection", "Keep-Alive")])
 
194
                self.assertHeader("Content-Length")
 
195
                self.assertHeader("Connection", "Keep-Alive")
 
196
                self.assertNoHeader("Transfer-Encoding")
 
197
                self.assertStatus('200 OK')
 
198
                self.assertBody('0123456789')
 
199
            else:
 
200
                # When a Content-Length is not provided,
 
201
                # the server should close the connection.
 
202
                self.getPage("/stream", headers=[("Connection", "Keep-Alive")])
 
203
                self.assertStatus('200 OK')
 
204
                self.assertBody('0123456789')
 
205
                
 
206
                self.assertNoHeader("Content-Length")
 
207
                self.assertNoHeader("Connection", "Keep-Alive")
 
208
                self.assertNoHeader("Transfer-Encoding")
 
209
                
 
210
                # Make another request on the same connection, which should error.
 
211
                self.assertRaises(NotConnected, self.getPage, "/")
 
212
    
 
213
    def test_HTTP10_KeepAlive(self):
 
214
        self.PROTOCOL = "HTTP/1.0"
 
215
        if self.scheme == "https":
 
216
            self.HTTP_CONN = HTTPSConnection
 
217
        else:
 
218
            self.HTTP_CONN = HTTPConnection
 
219
        
 
220
        # Test a normal HTTP/1.0 request.
 
221
        self.getPage("/page2")
 
222
        self.assertStatus('200 OK')
 
223
        self.assertBody(pov)
 
224
        # Apache, for example, may emit a Connection header even for HTTP/1.0
 
225
##        self.assertNoHeader("Connection")
 
226
        
 
227
        # Test a keep-alive HTTP/1.0 request.
 
228
        self.persistent = True
 
229
        
 
230
        self.getPage("/page3", headers=[("Connection", "Keep-Alive")])
 
231
        self.assertStatus('200 OK')
 
232
        self.assertBody(pov)
 
233
        self.assertHeader("Connection", "Keep-Alive")
 
234
        
 
235
        # Remove the keep-alive header again.
 
236
        self.getPage("/page3")
 
237
        self.assertStatus('200 OK')
 
238
        self.assertBody(pov)
 
239
        # Apache, for example, may emit a Connection header even for HTTP/1.0
 
240
##        self.assertNoHeader("Connection")
 
241
 
 
242
 
 
243
class PipelineTests(helper.CPWebCase):
 
244
    
 
245
    def test_HTTP11_Timeout(self):
 
246
        # If we timeout without sending any data,
 
247
        # the server will close the conn with a 408.
 
248
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
249
            return self.skip()
 
250
        
 
251
        self.PROTOCOL = "HTTP/1.1"
 
252
        
 
253
        # Connect but send nothing.
 
254
        self.persistent = True
 
255
        conn = self.HTTP_CONN
 
256
        conn.auto_open = False
 
257
        conn.connect()
 
258
        
 
259
        # Wait for our socket timeout
 
260
        time.sleep(timeout * 2)
 
261
        
 
262
        # The request should have returned 408 already.
 
263
        response = conn.response_class(conn.sock, method="GET")
 
264
        response.begin()
 
265
        self.assertEqual(response.status, 408)
 
266
        conn.close()
 
267
        
 
268
        # Connect but send half the headers only.
 
269
        self.persistent = True
 
270
        conn = self.HTTP_CONN
 
271
        conn.auto_open = False
 
272
        conn.connect()
 
273
        conn.send('GET /hello HTTP/1.1')
 
274
        conn.send(("Host: %s" % self.HOST).encode('ascii'))
 
275
        
 
276
        # Wait for our socket timeout
 
277
        time.sleep(timeout * 2)
 
278
        
 
279
        # The conn should have already sent 408.
 
280
        response = conn.response_class(conn.sock, method="GET")
 
281
        response.begin()
 
282
        self.assertEqual(response.status, 408)
 
283
        conn.close()
 
284
    
 
285
    def test_HTTP11_Timeout_after_request(self):
 
286
        # If we timeout after at least one request has succeeded,
 
287
        # the server will close the conn without 408.
 
288
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
289
            return self.skip()
 
290
        
 
291
        self.PROTOCOL = "HTTP/1.1"
 
292
        
 
293
        # Make an initial request
 
294
        self.persistent = True
 
295
        conn = self.HTTP_CONN
 
296
        conn.putrequest("GET", "/timeout?t=%s" % timeout, skip_host=True)
 
297
        conn.putheader("Host", self.HOST)
 
298
        conn.endheaders()
 
299
        response = conn.response_class(conn.sock, method="GET")
 
300
        response.begin()
 
301
        self.assertEqual(response.status, 200)
 
302
        self.body = response.read()
 
303
        self.assertBody(str(timeout))
 
304
        
 
305
        # Make a second request on the same socket
 
306
        conn._output('GET /hello HTTP/1.1')
 
307
        conn._output("Host: %s" % self.HOST)
 
308
        conn._send_output()
 
309
        response = conn.response_class(conn.sock, method="GET")
 
310
        response.begin()
 
311
        self.assertEqual(response.status, 200)
 
312
        self.body = response.read()
 
313
        self.assertBody("Hello, world!")
 
314
        
 
315
        # Wait for our socket timeout
 
316
        time.sleep(timeout * 2)
 
317
        
 
318
        # Make another request on the same socket, which should error
 
319
        conn._output('GET /hello HTTP/1.1')
 
320
        conn._output("Host: %s" % self.HOST)
 
321
        conn._send_output()
 
322
        response = conn.response_class(conn.sock, method="GET")
 
323
        try:
 
324
            response.begin()
 
325
        except:
 
326
            if not isinstance(sys.exc_info()[1],
 
327
                              (socket.error, BadStatusLine)):
 
328
                self.fail("Writing to timed out socket didn't fail"
 
329
                          " as it should have: %s" % sys.exc_info()[1])
 
330
        else:
 
331
            if response.status != 408:
 
332
                self.fail("Writing to timed out socket didn't fail"
 
333
                          " as it should have: %s" %
 
334
                          response.read())
 
335
        
 
336
        conn.close()
 
337
        
 
338
        # Make another request on a new socket, which should work
 
339
        self.persistent = True
 
340
        conn = self.HTTP_CONN
 
341
        conn.putrequest("GET", "/", skip_host=True)
 
342
        conn.putheader("Host", self.HOST)
 
343
        conn.endheaders()
 
344
        response = conn.response_class(conn.sock, method="GET")
 
345
        response.begin()
 
346
        self.assertEqual(response.status, 200)
 
347
        self.body = response.read()
 
348
        self.assertBody(pov)
 
349
 
 
350
        
 
351
        # Make another request on the same socket,
 
352
        # but timeout on the headers
 
353
        conn.send('GET /hello HTTP/1.1')
 
354
        # Wait for our socket timeout
 
355
        time.sleep(timeout * 2)
 
356
        response = conn.response_class(conn.sock, method="GET")
 
357
        try:
 
358
            response.begin()
 
359
        except:
 
360
            if not isinstance(sys.exc_info()[1],
 
361
                              (socket.error, BadStatusLine)):
 
362
                self.fail("Writing to timed out socket didn't fail"
 
363
                          " as it should have: %s" % sys.exc_info()[1])
 
364
        else:
 
365
            self.fail("Writing to timed out socket didn't fail"
 
366
                      " as it should have: %s" %
 
367
                      response.read())
 
368
        
 
369
        conn.close()
 
370
        
 
371
        # Retry the request on a new connection, which should work
 
372
        self.persistent = True
 
373
        conn = self.HTTP_CONN
 
374
        conn.putrequest("GET", "/", skip_host=True)
 
375
        conn.putheader("Host", self.HOST)
 
376
        conn.endheaders()
 
377
        response = conn.response_class(conn.sock, method="GET")
 
378
        response.begin()
 
379
        self.assertEqual(response.status, 200)
 
380
        self.body = response.read()
 
381
        self.assertBody(pov)
 
382
        conn.close()
 
383
    
 
384
    def test_HTTP11_pipelining(self):
 
385
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
386
            return self.skip()
 
387
        
 
388
        self.PROTOCOL = "HTTP/1.1"
 
389
        
 
390
        # Test pipelining. httplib doesn't support this directly.
 
391
        self.persistent = True
 
392
        conn = self.HTTP_CONN
 
393
        
 
394
        # Put request 1
 
395
        conn.putrequest("GET", "/hello", skip_host=True)
 
396
        conn.putheader("Host", self.HOST)
 
397
        conn.endheaders()
 
398
        
 
399
        for trial in range(5):
 
400
            # Put next request
 
401
            conn._output('GET /hello HTTP/1.1')
 
402
            conn._output("Host: %s" % self.HOST)
 
403
            conn._send_output()
 
404
            
 
405
            # Retrieve previous response
 
406
            response = conn.response_class(conn.sock, method="GET")
 
407
            response.begin()
 
408
            body = response.read()
 
409
            self.assertEqual(response.status, 200)
 
410
            self.assertEqual(body, "Hello, world!")
 
411
        
 
412
        # Retrieve final response
 
413
        response = conn.response_class(conn.sock, method="GET")
 
414
        response.begin()
 
415
        body = response.read()
 
416
        self.assertEqual(response.status, 200)
 
417
        self.assertEqual(body, "Hello, world!")
 
418
        
 
419
        conn.close()
 
420
    
 
421
    def test_100_Continue(self):
 
422
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
423
            return self.skip()
 
424
        
 
425
        self.PROTOCOL = "HTTP/1.1"
 
426
        
 
427
        self.persistent = True
 
428
        conn = self.HTTP_CONN
 
429
        
 
430
        # Try a page without an Expect request header first.
 
431
        # Note that httplib's response.begin automatically ignores
 
432
        # 100 Continue responses, so we must manually check for it.
 
433
        conn.putrequest("POST", "/upload", skip_host=True)
 
434
        conn.putheader("Host", self.HOST)
 
435
        conn.putheader("Content-Type", "text/plain")
 
436
        conn.putheader("Content-Length", "4")
 
437
        conn.endheaders()
 
438
        conn.send("d'oh")
 
439
        response = conn.response_class(conn.sock, method="POST")
 
440
        version, status, reason = response._read_status()
 
441
        self.assertNotEqual(status, 100)
 
442
        conn.close()
 
443
        
 
444
        # Now try a page with an Expect header...
 
445
        conn.connect()
 
446
        conn.putrequest("POST", "/upload", skip_host=True)
 
447
        conn.putheader("Host", self.HOST)
 
448
        conn.putheader("Content-Type", "text/plain")
 
449
        conn.putheader("Content-Length", "17")
 
450
        conn.putheader("Expect", "100-continue")
 
451
        conn.endheaders()
 
452
        response = conn.response_class(conn.sock, method="POST")
 
453
        
 
454
        # ...assert and then skip the 100 response
 
455
        version, status, reason = response._read_status()
 
456
        self.assertEqual(status, 100)
 
457
        while True:
 
458
            line = response.fp.readline().strip()
 
459
            if line:
 
460
                self.fail("100 Continue should not output any headers. Got %r" % line)
 
461
            else:
 
462
                break
 
463
        
 
464
        # ...send the body
 
465
        conn.send("I am a small file")
 
466
        
 
467
        # ...get the final response
 
468
        response.begin()
 
469
        self.status, self.headers, self.body = webtest.shb(response)
 
470
        self.assertStatus(200)
 
471
        self.assertBody("thanks for 'I am a small file'")
 
472
        conn.close()
 
473
 
 
474
 
 
475
class ConnectionTests(helper.CPWebCase):
 
476
    
 
477
    def test_readall_or_close(self):
 
478
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
479
            return self.skip()
 
480
        
 
481
        self.PROTOCOL = "HTTP/1.1"
 
482
        
 
483
        if self.scheme == "https":
 
484
            self.HTTP_CONN = HTTPSConnection
 
485
        else:
 
486
            self.HTTP_CONN = HTTPConnection
 
487
        
 
488
        # Test a max of 0 (the default) and then reset to what it was above.
 
489
        old_max = cherrypy.server.max_request_body_size
 
490
        for new_max in (0, old_max):
 
491
            cherrypy.server.max_request_body_size = new_max
 
492
            
 
493
            self.persistent = True
 
494
            conn = self.HTTP_CONN
 
495
            
 
496
            # Get a POST page with an error
 
497
            conn.putrequest("POST", "/err_before_read", skip_host=True)
 
498
            conn.putheader("Host", self.HOST)
 
499
            conn.putheader("Content-Type", "text/plain")
 
500
            conn.putheader("Content-Length", "1000")
 
501
            conn.putheader("Expect", "100-continue")
 
502
            conn.endheaders()
 
503
            response = conn.response_class(conn.sock, method="POST")
 
504
            
 
505
            # ...assert and then skip the 100 response
 
506
            version, status, reason = response._read_status()
 
507
            self.assertEqual(status, 100)
 
508
            while True:
 
509
                skip = response.fp.readline().strip()
 
510
                if not skip:
 
511
                    break
 
512
            
 
513
            # ...send the body
 
514
            conn.send("x" * 1000)
 
515
            
 
516
            # ...get the final response
 
517
            response.begin()
 
518
            self.status, self.headers, self.body = webtest.shb(response)
 
519
            self.assertStatus(500)
 
520
            
 
521
            # Now try a working page with an Expect header...
 
522
            conn._output('POST /upload HTTP/1.1')
 
523
            conn._output("Host: %s" % self.HOST)
 
524
            conn._output("Content-Type: text/plain")
 
525
            conn._output("Content-Length: 17")
 
526
            conn._output("Expect: 100-continue")
 
527
            conn._send_output()
 
528
            response = conn.response_class(conn.sock, method="POST")
 
529
            
 
530
            # ...assert and then skip the 100 response
 
531
            version, status, reason = response._read_status()
 
532
            self.assertEqual(status, 100)
 
533
            while True:
 
534
                skip = response.fp.readline().strip()
 
535
                if not skip:
 
536
                    break
 
537
            
 
538
            # ...send the body
 
539
            conn.send("I am a small file")
 
540
            
 
541
            # ...get the final response
 
542
            response.begin()
 
543
            self.status, self.headers, self.body = webtest.shb(response)
 
544
            self.assertStatus(200)
 
545
            self.assertBody("thanks for 'I am a small file'")
 
546
            conn.close()
 
547
    
 
548
    def test_No_Message_Body(self):
 
549
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
550
            return self.skip()
 
551
        
 
552
        self.PROTOCOL = "HTTP/1.1"
 
553
        
 
554
        # Set our HTTP_CONN to an instance so it persists between requests.
 
555
        self.persistent = True
 
556
        
 
557
        # Make the first request and assert there's no "Connection: close".
 
558
        self.getPage("/")
 
559
        self.assertStatus('200 OK')
 
560
        self.assertBody(pov)
 
561
        self.assertNoHeader("Connection")
 
562
        
 
563
        # Make a 204 request on the same connection.
 
564
        self.getPage("/custom/204")
 
565
        self.assertStatus(204)
 
566
        self.assertNoHeader("Content-Length")
 
567
        self.assertBody("")
 
568
        self.assertNoHeader("Connection")
 
569
        
 
570
        # Make a 304 request on the same connection.
 
571
        self.getPage("/custom/304")
 
572
        self.assertStatus(304)
 
573
        self.assertNoHeader("Content-Length")
 
574
        self.assertBody("")
 
575
        self.assertNoHeader("Connection")
 
576
    
 
577
    def test_Chunked_Encoding(self):
 
578
        if cherrypy.server.protocol_version != "HTTP/1.1":
 
579
            return self.skip()
 
580
        
 
581
        if (hasattr(self, 'harness') and
 
582
            "modpython" in self.harness.__class__.__name__.lower()):
 
583
            # mod_python forbids chunked encoding
 
584
            return self.skip()
 
585
        
 
586
        self.PROTOCOL = "HTTP/1.1"
 
587
        
 
588
        # Set our HTTP_CONN to an instance so it persists between requests.
 
589
        self.persistent = True
 
590
        conn = self.HTTP_CONN
 
591
        
 
592
        # Try a normal chunked request (with extensions)
 
593
        body = ("8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n"
 
594
                "Content-Type: application/json\r\n"
 
595
                "\r\n")
 
596
        conn.putrequest("POST", "/upload", skip_host=True)
 
597
        conn.putheader("Host", self.HOST)
 
598
        conn.putheader("Transfer-Encoding", "chunked")
 
599
        conn.putheader("Trailer", "Content-Type")
 
600
        # Note that this is somewhat malformed:
 
601
        # we shouldn't be sending Content-Length.
 
602
        # RFC 2616 says the server should ignore it.
 
603
        conn.putheader("Content-Length", "3")
 
604
        conn.endheaders()
 
605
        conn.send(body)
 
606
        response = conn.getresponse()
 
607
        self.status, self.headers, self.body = webtest.shb(response)
 
608
        self.assertStatus('200 OK')
 
609
        self.assertBody("thanks for 'xx\r\nxxxxyyyyy'")
 
610
        
 
611
        # Try a chunked request that exceeds server.max_request_body_size.
 
612
        # Note that the delimiters and trailer are included.
 
613
        body = "3e3\r\n" + ("x" * 995) + "\r\n0\r\n\r\n"
 
614
        conn.putrequest("POST", "/upload", skip_host=True)
 
615
        conn.putheader("Host", self.HOST)
 
616
        conn.putheader("Transfer-Encoding", "chunked")
 
617
        conn.putheader("Content-Type", "text/plain")
 
618
        # Chunked requests don't need a content-length
 
619
##        conn.putheader("Content-Length", len(body))
 
620
        conn.endheaders()
 
621
        conn.send(body)
 
622
        response = conn.getresponse()
 
623
        self.status, self.headers, self.body = webtest.shb(response)
 
624
        self.assertStatus(413)
 
625
        conn.close()
 
626
    
 
627
    def test_Content_Length(self):
 
628
        # Try a non-chunked request where Content-Length exceeds
 
629
        # server.max_request_body_size. Assert error before body send.
 
630
        self.persistent = True
 
631
        conn = self.HTTP_CONN
 
632
        conn.putrequest("POST", "/upload", skip_host=True)
 
633
        conn.putheader("Host", self.HOST)
 
634
        conn.putheader("Content-Type", "text/plain")
 
635
        conn.putheader("Content-Length", "9999")
 
636
        conn.endheaders()
 
637
        response = conn.getresponse()
 
638
        self.status, self.headers, self.body = webtest.shb(response)
 
639
        self.assertStatus(413)
 
640
        self.assertBody("")
 
641
        conn.close()
 
642
    
 
643
    def test_598(self):
 
644
        remote_data_conn = urllib.urlopen('%s://%s:%s/one_megabyte_of_a/' %
 
645
                                          (self.scheme, self.HOST, self.PORT,))
 
646
        buf = remote_data_conn.read(512)
 
647
        time.sleep(timeout * 0.6)
 
648
        remaining = (1024 * 1024) - 512
 
649
        while remaining:
 
650
            data = remote_data_conn.read(remaining)
 
651
            if not data:
 
652
                break
 
653
            else:
 
654
                buf += data
 
655
            remaining -= len(data)
 
656
       
 
657
        self.assertEqual(len(buf), 1024 * 1024)
 
658
        self.assertEqual(buf, "a" * 1024 * 1024)
 
659
        self.assertEqual(remaining, 0)
 
660
        remote_data_conn.close()
 
661
 
 
662
 
 
663
class BadRequestTests(helper.CPWebCase):
 
664
    
 
665
    def test_No_CRLF(self):
 
666
        self.persistent = True
 
667
        
 
668
        conn = self.HTTP_CONN
 
669
        conn.send('GET /hello HTTP/1.1\n\n')
 
670
        response = conn.response_class(conn.sock, method="GET")
 
671
        response.begin()
 
672
        self.body = response.read()
 
673
        self.assertBody("HTTP requires CRLF terminators")
 
674
        conn.close()
 
675
        
 
676
        conn.connect()
 
677
        conn.send('GET /hello HTTP/1.1\r\n\n')
 
678
        response = conn.response_class(conn.sock, method="GET")
 
679
        response.begin()
 
680
        self.body = response.read()
 
681
        self.assertBody("HTTP requires CRLF terminators")
 
682
        conn.close()
 
683
 
 
684
 
 
685
 
 
686
if __name__ == "__main__":
 
687
    helper.testmain()