test_conn.py (20852B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\test\test_conn.py 2 import socket 3 import sys 4 import time 5 timeout = 1 6 import cherrypy 7 from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, NotConnected, BadStatusLine 8 from cherrypy._cpcompat import ntob, urlopen, unicodestr 9 from cherrypy.test import webtest 10 from cherrypy import _cperror 11 pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN' 12 13 def setup_server(): 14 15 def raise500(): 16 raise cherrypy.HTTPError(500) 17 18 class Root: 19 20 def index(self): 21 return pov 22 23 index.exposed = True 24 page1 = index 25 page2 = index 26 page3 = index 27 28 def hello(self): 29 return 'Hello, world!' 30 31 hello.exposed = True 32 33 def timeout(self, t): 34 return str(cherrypy.server.httpserver.timeout) 35 36 timeout.exposed = True 37 38 def stream(self, set_cl = False): 39 if set_cl: 40 cherrypy.response.headers['Content-Length'] = 10 41 42 def content(): 43 for x in range(10): 44 yield str(x) 45 46 return content() 47 48 stream.exposed = True 49 stream._cp_config = {'response.stream': True} 50 51 def error(self, code = 500): 52 raise cherrypy.HTTPError(code) 53 54 error.exposed = True 55 56 def upload(self): 57 if not cherrypy.request.method == 'POST': 58 raise AssertionError("'POST' != request.method %r" % cherrypy.request.method) 59 return "thanks for '%s'" % cherrypy.request.body.read() 60 61 upload.exposed = True 62 63 def custom(self, response_code): 64 cherrypy.response.status = response_code 65 return 'Code = %s' % response_code 66 67 custom.exposed = True 68 69 def err_before_read(self): 70 return 'ok' 71 72 err_before_read.exposed = True 73 err_before_read._cp_config = {'hooks.on_start_resource': raise500} 74 75 def one_megabyte_of_a(self): 76 return ['a' * 1024] * 1024 77 78 one_megabyte_of_a.exposed = True 79 80 def custom_cl(self, body, cl): 81 cherrypy.response.headers['Content-Length'] = cl 82 if not isinstance(body, list): 83 body = [body] 84 newbody = [] 85 for chunk in body: 86 if isinstance(chunk, unicodestr): 87 chunk = chunk.encode('ISO-8859-1') 88 newbody.append(chunk) 89 90 return newbody 91 92 custom_cl.exposed = True 93 custom_cl._cp_config = {'tools.encode.on': False} 94 95 cherrypy.tree.mount(Root()) 96 cherrypy.config.update({'server.max_request_body_size': 1001, 97 'server.socket_timeout': timeout}) 98 99 100 from cherrypy.test import helper 101 102 class ConnectionCloseTests(helper.CPWebCase): 103 setup_server = staticmethod(setup_server) 104 105 def test_HTTP11(self): 106 if cherrypy.server.protocol_version != 'HTTP/1.1': 107 return self.skip() 108 self.PROTOCOL = 'HTTP/1.1' 109 self.persistent = True 110 self.getPage('/') 111 self.assertStatus('200 OK') 112 self.assertBody(pov) 113 self.assertNoHeader('Connection') 114 self.getPage('/page1') 115 self.assertStatus('200 OK') 116 self.assertBody(pov) 117 self.assertNoHeader('Connection') 118 self.getPage('/page2', headers=[('Connection', 'close')]) 119 self.assertStatus('200 OK') 120 self.assertBody(pov) 121 self.assertHeader('Connection', 'close') 122 self.assertRaises(NotConnected, self.getPage, '/') 123 124 def test_Streaming_no_len(self): 125 self._streaming(set_cl=False) 126 127 def test_Streaming_with_len(self): 128 self._streaming(set_cl=True) 129 130 def _streaming(self, set_cl): 131 if cherrypy.server.protocol_version == 'HTTP/1.1': 132 self.PROTOCOL = 'HTTP/1.1' 133 self.persistent = True 134 self.getPage('/') 135 self.assertStatus('200 OK') 136 self.assertBody(pov) 137 self.assertNoHeader('Connection') 138 if set_cl: 139 self.getPage('/stream?set_cl=Yes') 140 self.assertHeader('Content-Length') 141 self.assertNoHeader('Connection', 'close') 142 self.assertNoHeader('Transfer-Encoding') 143 self.assertStatus('200 OK') 144 self.assertBody('0123456789') 145 else: 146 self.getPage('/stream') 147 self.assertNoHeader('Content-Length') 148 self.assertStatus('200 OK') 149 self.assertBody('0123456789') 150 chunked_response = False 151 for k, v in self.headers: 152 if k.lower() == 'transfer-encoding': 153 if str(v) == 'chunked': 154 chunked_response = True 155 156 if chunked_response: 157 self.assertNoHeader('Connection', 'close') 158 else: 159 self.assertHeader('Connection', 'close') 160 self.assertRaises(NotConnected, self.getPage, '/') 161 self.getPage('/stream', method='HEAD') 162 self.assertStatus('200 OK') 163 self.assertBody('') 164 self.assertNoHeader('Transfer-Encoding') 165 else: 166 self.PROTOCOL = 'HTTP/1.0' 167 self.persistent = True 168 self.getPage('/', headers=[('Connection', 'Keep-Alive')]) 169 self.assertStatus('200 OK') 170 self.assertBody(pov) 171 self.assertHeader('Connection', 'Keep-Alive') 172 if set_cl: 173 self.getPage('/stream?set_cl=Yes', headers=[('Connection', 'Keep-Alive')]) 174 self.assertHeader('Content-Length') 175 self.assertHeader('Connection', 'Keep-Alive') 176 self.assertNoHeader('Transfer-Encoding') 177 self.assertStatus('200 OK') 178 self.assertBody('0123456789') 179 else: 180 self.getPage('/stream', headers=[('Connection', 'Keep-Alive')]) 181 self.assertStatus('200 OK') 182 self.assertBody('0123456789') 183 self.assertNoHeader('Content-Length') 184 self.assertNoHeader('Connection', 'Keep-Alive') 185 self.assertNoHeader('Transfer-Encoding') 186 self.assertRaises(NotConnected, self.getPage, '/') 187 188 def test_HTTP10_KeepAlive(self): 189 self.PROTOCOL = 'HTTP/1.0' 190 if self.scheme == 'https': 191 self.HTTP_CONN = HTTPSConnection 192 else: 193 self.HTTP_CONN = HTTPConnection 194 self.getPage('/page2') 195 self.assertStatus('200 OK') 196 self.assertBody(pov) 197 self.persistent = True 198 self.getPage('/page3', headers=[('Connection', 'Keep-Alive')]) 199 self.assertStatus('200 OK') 200 self.assertBody(pov) 201 self.assertHeader('Connection', 'Keep-Alive') 202 self.getPage('/page3') 203 self.assertStatus('200 OK') 204 self.assertBody(pov) 205 206 207 class PipelineTests(helper.CPWebCase): 208 setup_server = staticmethod(setup_server) 209 210 def test_HTTP11_Timeout(self): 211 if cherrypy.server.protocol_version != 'HTTP/1.1': 212 return self.skip() 213 self.PROTOCOL = 'HTTP/1.1' 214 self.persistent = True 215 conn = self.HTTP_CONN 216 conn.auto_open = False 217 conn.connect() 218 time.sleep(timeout * 2) 219 response = conn.response_class(conn.sock, method='GET') 220 response.begin() 221 self.assertEqual(response.status, 408) 222 conn.close() 223 self.persistent = True 224 conn = self.HTTP_CONN 225 conn.auto_open = False 226 conn.connect() 227 conn.send(ntob('GET /hello HTTP/1.1')) 228 conn.send(('Host: %s' % self.HOST).encode('ascii')) 229 time.sleep(timeout * 2) 230 response = conn.response_class(conn.sock, method='GET') 231 response.begin() 232 self.assertEqual(response.status, 408) 233 conn.close() 234 235 def test_HTTP11_Timeout_after_request(self): 236 if cherrypy.server.protocol_version != 'HTTP/1.1': 237 return self.skip() 238 self.PROTOCOL = 'HTTP/1.1' 239 self.persistent = True 240 conn = self.HTTP_CONN 241 conn.putrequest('GET', '/timeout?t=%s' % timeout, skip_host=True) 242 conn.putheader('Host', self.HOST) 243 conn.endheaders() 244 response = conn.response_class(conn.sock, method='GET') 245 response.begin() 246 self.assertEqual(response.status, 200) 247 self.body = response.read() 248 self.assertBody(str(timeout)) 249 conn._output(ntob('GET /hello HTTP/1.1')) 250 conn._output(ntob('Host: %s' % self.HOST, 'ascii')) 251 conn._send_output() 252 response = conn.response_class(conn.sock, method='GET') 253 response.begin() 254 self.assertEqual(response.status, 200) 255 self.body = response.read() 256 self.assertBody('Hello, world!') 257 time.sleep(timeout * 2) 258 conn._output(ntob('GET /hello HTTP/1.1')) 259 conn._output(ntob('Host: %s' % self.HOST, 'ascii')) 260 conn._send_output() 261 response = conn.response_class(conn.sock, method='GET') 262 try: 263 response.begin() 264 except: 265 if not isinstance(sys.exc_info()[1], (socket.error, BadStatusLine)): 266 self.fail("Writing to timed out socket didn't fail as it should have: %s" % sys.exc_info()[1]) 267 else: 268 if response.status != 408: 269 self.fail("Writing to timed out socket didn't fail as it should have: %s" % response.read()) 270 271 conn.close() 272 self.persistent = True 273 conn = self.HTTP_CONN 274 conn.putrequest('GET', '/', skip_host=True) 275 conn.putheader('Host', self.HOST) 276 conn.endheaders() 277 response = conn.response_class(conn.sock, method='GET') 278 response.begin() 279 self.assertEqual(response.status, 200) 280 self.body = response.read() 281 self.assertBody(pov) 282 conn.send(ntob('GET /hello HTTP/1.1')) 283 time.sleep(timeout * 2) 284 response = conn.response_class(conn.sock, method='GET') 285 try: 286 response.begin() 287 except: 288 if not isinstance(sys.exc_info()[1], (socket.error, BadStatusLine)): 289 self.fail("Writing to timed out socket didn't fail as it should have: %s" % sys.exc_info()[1]) 290 else: 291 self.fail("Writing to timed out socket didn't fail as it should have: %s" % response.read()) 292 293 conn.close() 294 self.persistent = True 295 conn = self.HTTP_CONN 296 conn.putrequest('GET', '/', 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(pov) 304 conn.close() 305 306 def test_HTTP11_pipelining(self): 307 if cherrypy.server.protocol_version != 'HTTP/1.1': 308 return self.skip() 309 self.PROTOCOL = 'HTTP/1.1' 310 self.persistent = True 311 conn = self.HTTP_CONN 312 conn.putrequest('GET', '/hello', skip_host=True) 313 conn.putheader('Host', self.HOST) 314 conn.endheaders() 315 for trial in range(5): 316 conn._output(ntob('GET /hello HTTP/1.1')) 317 conn._output(ntob('Host: %s' % self.HOST, 'ascii')) 318 conn._send_output() 319 response = conn.response_class(conn.sock, method='GET') 320 response.begin() 321 body = response.read(13) 322 self.assertEqual(response.status, 200) 323 self.assertEqual(body, ntob('Hello, world!')) 324 325 response = conn.response_class(conn.sock, method='GET') 326 response.begin() 327 body = response.read() 328 self.assertEqual(response.status, 200) 329 self.assertEqual(body, ntob('Hello, world!')) 330 conn.close() 331 332 def test_100_Continue(self): 333 if cherrypy.server.protocol_version != 'HTTP/1.1': 334 return self.skip() 335 self.PROTOCOL = 'HTTP/1.1' 336 self.persistent = True 337 conn = self.HTTP_CONN 338 conn.putrequest('POST', '/upload', skip_host=True) 339 conn.putheader('Host', self.HOST) 340 conn.putheader('Content-Type', 'text/plain') 341 conn.putheader('Content-Length', '4') 342 conn.endheaders() 343 conn.send(ntob("d'oh")) 344 response = conn.response_class(conn.sock, method='POST') 345 version, status, reason = response._read_status() 346 self.assertNotEqual(status, 100) 347 conn.close() 348 conn.connect() 349 conn.putrequest('POST', '/upload', skip_host=True) 350 conn.putheader('Host', self.HOST) 351 conn.putheader('Content-Type', 'text/plain') 352 conn.putheader('Content-Length', '17') 353 conn.putheader('Expect', '100-continue') 354 conn.endheaders() 355 response = conn.response_class(conn.sock, method='POST') 356 version, status, reason = response._read_status() 357 self.assertEqual(status, 100) 358 while True: 359 line = response.fp.readline().strip() 360 if line: 361 self.fail('100 Continue should not output any headers. Got %r' % line) 362 else: 363 break 364 365 body = ntob('I am a small file') 366 conn.send(body) 367 response.begin() 368 self.status, self.headers, self.body = webtest.shb(response) 369 self.assertStatus(200) 370 self.assertBody("thanks for '%s'" % body) 371 conn.close() 372 373 374 class ConnectionTests(helper.CPWebCase): 375 setup_server = staticmethod(setup_server) 376 377 def test_readall_or_close(self): 378 if cherrypy.server.protocol_version != 'HTTP/1.1': 379 return self.skip() 380 self.PROTOCOL = 'HTTP/1.1' 381 if self.scheme == 'https': 382 self.HTTP_CONN = HTTPSConnection 383 else: 384 self.HTTP_CONN = HTTPConnection 385 old_max = cherrypy.server.max_request_body_size 386 for new_max in (0, old_max): 387 cherrypy.server.max_request_body_size = new_max 388 self.persistent = True 389 conn = self.HTTP_CONN 390 conn.putrequest('POST', '/err_before_read', skip_host=True) 391 conn.putheader('Host', self.HOST) 392 conn.putheader('Content-Type', 'text/plain') 393 conn.putheader('Content-Length', '1000') 394 conn.putheader('Expect', '100-continue') 395 conn.endheaders() 396 response = conn.response_class(conn.sock, method='POST') 397 version, status, reason = response._read_status() 398 self.assertEqual(status, 100) 399 while True: 400 skip = response.fp.readline().strip() 401 if not skip: 402 break 403 404 conn.send(ntob('x' * 1000)) 405 response.begin() 406 self.status, self.headers, self.body = webtest.shb(response) 407 self.assertStatus(500) 408 conn._output(ntob('POST /upload HTTP/1.1')) 409 conn._output(ntob('Host: %s' % self.HOST, 'ascii')) 410 conn._output(ntob('Content-Type: text/plain')) 411 conn._output(ntob('Content-Length: 17')) 412 conn._output(ntob('Expect: 100-continue')) 413 conn._send_output() 414 response = conn.response_class(conn.sock, method='POST') 415 version, status, reason = response._read_status() 416 self.assertEqual(status, 100) 417 while True: 418 skip = response.fp.readline().strip() 419 if not skip: 420 break 421 422 body = ntob('I am a small file') 423 conn.send(body) 424 response.begin() 425 self.status, self.headers, self.body = webtest.shb(response) 426 self.assertStatus(200) 427 self.assertBody("thanks for '%s'" % body) 428 conn.close() 429 430 def test_No_Message_Body(self): 431 if cherrypy.server.protocol_version != 'HTTP/1.1': 432 return self.skip() 433 self.PROTOCOL = 'HTTP/1.1' 434 self.persistent = True 435 self.getPage('/') 436 self.assertStatus('200 OK') 437 self.assertBody(pov) 438 self.assertNoHeader('Connection') 439 self.getPage('/custom/204') 440 self.assertStatus(204) 441 self.assertNoHeader('Content-Length') 442 self.assertBody('') 443 self.assertNoHeader('Connection') 444 self.getPage('/custom/304') 445 self.assertStatus(304) 446 self.assertNoHeader('Content-Length') 447 self.assertBody('') 448 self.assertNoHeader('Connection') 449 450 def test_Chunked_Encoding(self): 451 if cherrypy.server.protocol_version != 'HTTP/1.1': 452 return self.skip() 453 if hasattr(self, 'harness') and 'modpython' in self.harness.__class__.__name__.lower(): 454 return self.skip() 455 self.PROTOCOL = 'HTTP/1.1' 456 self.persistent = True 457 conn = self.HTTP_CONN 458 body = ntob('8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\nContent-Type: application/json\r\n\r\n') 459 conn.putrequest('POST', '/upload', skip_host=True) 460 conn.putheader('Host', self.HOST) 461 conn.putheader('Transfer-Encoding', 'chunked') 462 conn.putheader('Trailer', 'Content-Type') 463 conn.putheader('Content-Length', '3') 464 conn.endheaders() 465 conn.send(body) 466 response = conn.getresponse() 467 self.status, self.headers, self.body = webtest.shb(response) 468 self.assertStatus('200 OK') 469 self.assertBody("thanks for '%s'" % ntob('xx\r\nxxxxyyyyy')) 470 body = ntob('3e3\r\n' + 'x' * 995 + '\r\n0\r\n\r\n') 471 conn.putrequest('POST', '/upload', skip_host=True) 472 conn.putheader('Host', self.HOST) 473 conn.putheader('Transfer-Encoding', 'chunked') 474 conn.putheader('Content-Type', 'text/plain') 475 conn.endheaders() 476 conn.send(body) 477 response = conn.getresponse() 478 self.status, self.headers, self.body = webtest.shb(response) 479 self.assertStatus(413) 480 conn.close() 481 482 def test_Content_Length_in(self): 483 self.persistent = True 484 conn = self.HTTP_CONN 485 conn.putrequest('POST', '/upload', skip_host=True) 486 conn.putheader('Host', self.HOST) 487 conn.putheader('Content-Type', 'text/plain') 488 conn.putheader('Content-Length', '9999') 489 conn.endheaders() 490 response = conn.getresponse() 491 self.status, self.headers, self.body = webtest.shb(response) 492 self.assertStatus(413) 493 self.assertBody('The entity sent with the request exceeds the maximum allowed bytes.') 494 conn.close() 495 496 def test_Content_Length_out_preheaders(self): 497 self.persistent = True 498 conn = self.HTTP_CONN 499 conn.putrequest('GET', '/custom_cl?body=I+have+too+many+bytes&cl=5', skip_host=True) 500 conn.putheader('Host', self.HOST) 501 conn.endheaders() 502 response = conn.getresponse() 503 self.status, self.headers, self.body = webtest.shb(response) 504 self.assertStatus(500) 505 self.assertBody('The requested resource returned more bytes than the declared Content-Length.') 506 conn.close() 507 508 def test_Content_Length_out_postheaders(self): 509 self.persistent = True 510 conn = self.HTTP_CONN 511 conn.putrequest('GET', '/custom_cl?body=I+too&body=+have+too+many&cl=5', skip_host=True) 512 conn.putheader('Host', self.HOST) 513 conn.endheaders() 514 response = conn.getresponse() 515 self.status, self.headers, self.body = webtest.shb(response) 516 self.assertStatus(200) 517 self.assertBody('I too') 518 conn.close() 519 520 def test_598(self): 521 remote_data_conn = urlopen('%s://%s:%s/one_megabyte_of_a/' % (self.scheme, self.HOST, self.PORT)) 522 buf = remote_data_conn.read(512) 523 time.sleep(timeout * 0.6) 524 remaining = 1048064 525 while remaining: 526 data = remote_data_conn.read(remaining) 527 if not data: 528 break 529 else: 530 buf += data 531 remaining -= len(data) 532 533 self.assertEqual(len(buf), 1048576) 534 self.assertEqual(buf, ntob('a' * 1024 * 1024)) 535 self.assertEqual(remaining, 0) 536 remote_data_conn.close() 537 538 539 class BadRequestTests(helper.CPWebCase): 540 setup_server = staticmethod(setup_server) 541 542 def test_No_CRLF(self): 543 self.persistent = True 544 conn = self.HTTP_CONN 545 conn.send(ntob('GET /hello HTTP/1.1\n\n')) 546 response = conn.response_class(conn.sock, method='GET') 547 response.begin() 548 self.body = response.read() 549 self.assertBody('HTTP requires CRLF terminators') 550 conn.close() 551 conn.connect() 552 conn.send(ntob('GET /hello HTTP/1.1\r\n\n')) 553 response = conn.response_class(conn.sock, method='GET') 554 response.begin() 555 self.body = response.read() 556 self.assertBody('HTTP requires CRLF terminators') 557 conn.close()