Package cherrypy :: Package test :: Module test_conn
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.test.test_conn

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