Changeset 1465
- Timestamp:
- 12/02/06 13:50:55
- Files:
-
- trunk/cherrypy/_cprequest.py (modified) (1 diff)
- trunk/cherrypy/test/test_conn.py (modified) (10 diffs)
- trunk/cherrypy/wsgiserver.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cprequest.py
r1449 r1465 536 536 headers = self.headers 537 537 if self.stream: 538 headers.pop('Content-Length', None) 538 if dict.get(headers, 'Content-Length') is None: 539 dict.pop(headers, 'Content-Length', None) 539 540 elif code < 200 or code in (204, 304): 540 541 # "All 1xx (informational), 204 (no content), 541 542 # and 304 (not modified) responses MUST NOT 542 543 # include a message-body." 543 headers.pop('Content-Length', None)544 dict.pop(headers, 'Content-Length', None) 544 545 self.body = "" 545 546 else: trunk/cherrypy/test/test_conn.py
r1464 r1465 29 29 hello.exposed = True 30 30 31 def stream(self): 32 for x in xrange(10): 33 yield str(x) 31 def stream(self, set_cl=False): 32 if set_cl: 33 cherrypy.response.headers['Content-Length'] = 10 34 35 def content(): 36 for x in xrange(10): 37 yield str(x) 38 39 return content() 34 40 stream.exposed = True 35 41 stream._cp_config = {'response.stream': True} … … 57 63 class ConnectionTests(helper.CPWebCase): 58 64 59 def test_HTTP11(self): 60 if cherrypy.server.protocol_version != "HTTP/1.1": 61 print "skipped ", 62 return 63 64 self.PROTOCOL = "HTTP/1.1" 65 66 # Set our HTTP_CONN to an instance so it persists between requests. 65 def connect_persistent(self, auto_open=False): 66 """Set our HTTP_CONN to an instance so it persists between requests.""" 67 67 if self.scheme == "https": 68 68 self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 69 69 else: 70 70 self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 71 # Don't automatically re-connect72 self.HTTP_CONN.auto_open = False71 # Automatically re-connect? 72 self.HTTP_CONN.auto_open = auto_open 73 73 self.HTTP_CONN.connect() 74 return self.HTTP_CONN 75 76 def test_HTTP11(self): 77 if cherrypy.server.protocol_version != "HTTP/1.1": 78 print "skipped ", 79 return 80 81 self.PROTOCOL = "HTTP/1.1" 82 83 self.connect_persistent() 74 84 75 85 # Make the first request and assert there's no "Connection: close". … … 85 95 self.assertNoHeader("Connection") 86 96 87 # Make another, streamed request on the same connection.88 self.getPage("/stream")89 self.assertStatus('200 OK')90 self.assertBody('0123456789')91 self.assertNoHeader("Content-Length")92 # Streamed output will either close the connection, or use93 # chunked encoding, to determine transfer-length.94 chunked_response = False95 for k, v in self.headers:96 if k.lower() == "transfer-encoding":97 if str(v) == "chunked":98 chunked_response = True99 if not chunked_response:100 self.assertHeader("Connection", "close")101 102 # Make another request on the same connection, which should error.103 self.assertRaises(httplib.NotConnected, self.getPage, "/")104 105 97 # Test client-side close. 106 if self.scheme == "https":107 self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT)108 else:109 self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT)110 self.HTTP_CONN.auto_open = False111 self.HTTP_CONN.connect()112 98 self.getPage("/page2", headers=[("Connection", "close")]) 113 99 self.assertStatus('200 OK') … … 117 103 # Make another request on the same connection, which should error. 118 104 self.assertRaises(httplib.NotConnected, self.getPage, "/") 105 106 def test_Streaming_no_len(self): 107 self._streaming(set_cl=False) 108 109 def test_Streaming_with_len(self): 110 self._streaming(set_cl=True) 111 112 def _streaming(self, set_cl): 113 if cherrypy.server.protocol_version != "HTTP/1.1": 114 print "skipped ", 115 return 116 117 self.PROTOCOL = "HTTP/1.1" 118 119 self.connect_persistent() 120 121 # Make the first request and assert there's no "Connection: close". 122 self.getPage("/") 123 self.assertStatus('200 OK') 124 self.assertBody(pov) 125 self.assertNoHeader("Connection") 126 127 # Make another, streamed request on the same connection. 128 if set_cl: 129 # When a Content-Length is provided, the content should stream 130 # without closing the connection. 131 self.getPage("/stream?set_cl=Yes") 132 self.assertHeader("Content-Length") 133 self.assertNoHeader("Connection", "close") 134 self.assertNoHeader("Transfer-Encoding") 135 else: 136 # When no Content-Length response header is provided, 137 # streamed output will either close the connection, or use 138 # chunked encoding, to determine transfer-length. 139 self.getPage("/stream") 140 self.assertNoHeader("Content-Length") 141 142 chunked_response = False 143 for k, v in self.headers: 144 if k.lower() == "transfer-encoding": 145 if str(v) == "chunked": 146 chunked_response = True 147 148 if chunked_response: 149 self.assertNoHeader("Connection", "close") 150 else: 151 self.assertHeader("Connection", "close") 152 153 # Make another request on the same connection, which should error. 154 self.assertRaises(httplib.NotConnected, self.getPage, "/") 155 156 self.assertStatus('200 OK') 157 self.assertBody('0123456789') 119 158 120 159 def test_HTTP11_Timeout(self): … … 136 175 137 176 # Make an initial request 138 if self.scheme == "https": 139 conn = httplib.HTTPSConnection(self.HOST, self.PORT) 140 else: 141 conn = httplib.HTTPConnection(self.HOST, self.PORT) 142 conn.auto_open = False 143 conn.connect() 177 conn = self.connect_persistent() 144 178 conn.putrequest("GET", "/", skip_host=True) 145 179 conn.putheader("Host", self.HOST) … … 184 218 185 219 # Make another request on a new socket, which should work 186 if self.scheme == "https": 187 conn = httplib.HTTPSConnection(self.HOST, self.PORT) 188 else: 189 conn = httplib.HTTPConnection(self.HOST, self.PORT) 190 conn.auto_open = False 191 conn.connect() 220 conn = self.connect_persistent() 192 221 conn.putrequest("GET", "/", skip_host=True) 193 222 conn.putheader("Host", self.HOST) … … 210 239 211 240 # Test pipelining. httplib doesn't support this directly. 212 if self.scheme == "https": 213 conn = httplib.HTTPSConnection(self.HOST, self.PORT) 214 else: 215 conn = httplib.HTTPConnection(self.HOST, self.PORT) 216 conn.auto_open = False 217 conn.connect() 241 conn = self.connect_persistent() 218 242 219 243 # Put request 1 … … 251 275 self.PROTOCOL = "HTTP/1.1" 252 276 253 if self.scheme == "https": 254 conn = httplib.HTTPSConnection(self.HOST, self.PORT) 255 else: 256 conn = httplib.HTTPConnection(self.HOST, self.PORT) 257 conn.auto_open = False 258 conn.connect() 277 conn = self.connect_persistent() 259 278 260 279 # Try a page without an Expect request header first. … … 307 326 308 327 # Set our HTTP_CONN to an instance so it persists between requests. 309 if self.scheme == "https": 310 self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 311 else: 312 self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 313 # Don't automatically re-connect 314 self.HTTP_CONN.auto_open = False 315 self.HTTP_CONN.connect() 328 self.connect_persistent() 316 329 317 330 # Make the first request and assert there's no "Connection: close". … … 402 415 403 416 # Test a keep-alive HTTP/1.0 request. 404 if self.scheme == "https": 405 self.HTTP_CONN = httplib.HTTPSConnection(self.HOST, self.PORT) 406 else: 407 self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 408 self.HTTP_CONN.auto_open = False 409 self.HTTP_CONN.connect() 417 self.connect_persistent() 410 418 411 419 self.getPage("/page3", headers=[("Connection", "Keep-Alive")]) trunk/cherrypy/wsgiserver.py
r1464 r1465 62 62 self.sent_headers = False 63 63 self.close_connection = False 64 self.chunked_write = False 64 65 65 66 def parse_request(self): … … 276 277 277 278 def respond(self): 279 wfile = self.connection.wfile 278 280 response = self.wsgi_app(self.environ, self.start_response) 279 281 try: 280 282 for line in response: 281 self.write(line) 283 if not self.sent_headers: 284 self.sent_headers = True 285 self.send_headers() 286 if self.chunked_write: 287 wfile.write(hex(len(line))[2:]) 288 wfile.write("\r\n") 289 wfile.write(line) 290 wfile.write("\r\n") 291 else: 292 wfile.write(line) 293 wfile.flush() 282 294 finally: 283 295 if hasattr(response, "close"): … … 287 299 self.sent_headers = True 288 300 self.send_headers() 301 if self.chunked_write: 302 wfile.write("0\r\n\r\n") 303 wfile.flush() 289 304 290 305 def simple_response(self, status, msg=""): … … 327 342 328 343 def send_headers(self): 329 hkeys = [key.lower() for (key,value) in self.outheaders] 330 344 hkeys = [key.lower() for (key, value) in self.outheaders] 331 345 status = int(self.status[:3]) 332 if (self.response_protocol == 'HTTP/1.1' 333 and (# Request Entity Too Large. Close conn to avoid garbage. 334 status == 413 335 # No Content-Length. Close conn to determine transfer-length. 336 or ("content-length" not in hkeys and 337 # "All 1xx (informational), 204 (no content), 338 # and 304 (not modified) responses MUST NOT 339 # include a message-body." 340 status >= 200 and status not in (204, 304)))): 341 if "connection" not in hkeys: 342 self.outheaders.append(("Connection", "close")) 343 self.close_connection = True 346 347 if self.response_protocol == 'HTTP/1.1': 348 if status == 413: 349 # Request Entity Too Large. Close conn to avoid garbage. 350 self.close_connection = True 351 elif "content-length" not in hkeys: 352 if status in (200, 203, 206): 353 # Use the chunked transfer-coding 354 self.chunked_write = True 355 self.outheaders.append(("Transfer-Encoding", "chunked")) 356 # "All 1xx (informational), 204 (no content), 357 # and 304 (not modified) responses MUST NOT 358 # include a message-body." 359 elif status >= 200 and status not in (204, 205, 304): 360 # Close conn to determine transfer-length. 361 self.close_connection = True 362 363 if self.close_connection and "connection" not in hkeys: 364 self.outheaders.append(("Connection", "close")) 344 365 345 366 if "date" not in hkeys:

