Changeset 679
- Timestamp:
- 09/25/05 18:22:47
- Files:
-
- trunk/cherrypy/_cperror.py (modified) (1 diff)
- trunk/cherrypy/_cphttpserver.py (modified) (2 diffs)
- trunk/cherrypy/_cphttptools.py (modified) (7 diffs)
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/test/helper.py (modified) (1 diff)
- trunk/cherrypy/test/test.py (modified) (1 diff)
- trunk/cherrypy/test/test_core.py (modified) (4 diffs)
- trunk/cherrypy/test/test_gzip_filter.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cperror.py
r650 r679 123 123 # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html 124 124 if status is None: 125 if cherrypy.re quest.version >= "1.1":125 if cherrypy.response.version >= "1.1": 126 126 status = 303 127 127 else: trunk/cherrypy/_cphttpserver.py
r665 r679 85 85 cherrypy.response.purge__() 86 86 87 cherrypy.request.multithread = cherrypy.config.get("server.threadPool") > 1 87 tp = cherrypy.config.get("server.threadPool") 88 cherrypy.request.multithread = (tp > 1) 88 89 cherrypy.request.multiprocess = False 89 90 cherrypy.server.request(self.client_address, … … 93 94 self.rfile, "http") 94 95 wfile = self.wfile 95 wfile.write("%s %s\r\n" % (self.protocol_version, cherrypy.response.status)) 96 wfile.write("%s %s\r\n" % 97 (self.protocol_version, cherrypy.response.status)) 96 98 97 99 has_close_conn = False trunk/cherrypy/_cphttptools.py
r675 r679 301 301 # version number to write in the response, so we limit our output 302 302 # to min(req, server). We want the following output: 303 # request version server version response version features 304 # a 1.0 1.0 1.0 1.0 305 # b 1.0 1.1 1.1 1.0 306 # c 1.1 1.0 1.0 1.0 307 # d 1.1 1.1 1.1 1.1 303 # request server actual written supported response 304 # version version response version feature set (resp.v) 305 # a 1.0 1.0 1.0 1.0 306 # b 1.0 1.1 1.1 1.0 307 # c 1.1 1.0 1.0 1.0 308 # d 1.1 1.1 1.1 1.1 308 309 # Notice that, in (b), the response will be "HTTP/1.1" even though 309 310 # the client only understands 1.0. RFC 2616 10.5.6 says we should … … 312 313 server_v = cherrypy.config.get("server.protocolVersion", "HTTP/1.0") 313 314 server_v = Version.from_http(server_v) 314 cherrypy.request.version = min(request_v, server_v) 315 # cherrypy.response.version should be used to determine whether or 316 # not to include a given HTTP/1.1 feature in the response content. 317 cherrypy.response.version = min(request_v, server_v) 318 # cherrypy.request.version == request.protocol in a Version instance. 319 cherrypy.request.version = request_v 315 320 316 321 # build a paramMap dictionary from queryString … … 352 357 request.originalParamList = request.paramList 353 358 354 if cherrypy.re quest.version >= "1.1":359 if cherrypy.response.version >= "1.1": 355 360 # All Internet-based HTTP/1.1 servers MUST respond with a 400 356 361 # (Bad Request) status code to any HTTP/1.1 request message … … 577 582 checkStatus() 578 583 579 if cherrypy.response.body is None: 580 cherrypy.response.body = [] 581 582 if cherrypy.response.headerMap.get('Content-Length') is None: 583 if (cherrypy.request.version < "1.1" or 584 # OPTIONS requests MUST include a Content-Length of 0 if no body. 585 # Just punt and figure len for all OPTIONS requests. 586 cherrypy.request.method == "OPTIONS"): 587 588 content = ''.join([chunk for chunk in cherrypy.response.body]) 589 cherrypy.response.body = [content] 590 cherrypy.response.headerMap['Content-Length'] = len(content) 591 else: 592 try: 593 del cherrypy.response.headerMap['Content-Length'] 594 except KeyError: 595 pass 584 response = cherrypy.response 585 if response.body is None: 586 response.body = [] 587 588 stream = cherrypy.config.get("streamResponse", False) 589 # OPTIONS requests MUST include a Content-Length of 0 if no body. 590 # Just punt and figure Content-Length for all OPTIONS requests. 591 if cherrypy.request.method == "OPTIONS": 592 stream = False 593 594 if stream: 595 try: 596 del response.headerMap['Content-Length'] 597 except KeyError: 598 pass 599 else: 600 # Responses which are not streamed should have a Content-Length, 601 # but allow user code to set Content-Length if desired. 602 if response.headerMap.get('Content-Length') is None: 603 content = ''.join([chunk for chunk in response.body]) 604 response.body = [content] 605 response.headerMap['Content-Length'] = len(content) 596 606 597 607 # For some statuses, Internet Explorer 5+ shows "friendly error messages" … … 599 609 # Fix this by returning a body over that size (by adding whitespace). 600 610 # See http://support.microsoft.com/kb/q218155/ 601 s = int( cherrypy.response.status.split(" ")[0])611 s = int(response.status.split(" ")[0]) 602 612 s = _ie_friendly_error_sizes.get(s, 0) 603 613 if s: … … 605 615 # Since we are issuing an HTTP error status, we assume that 606 616 # the entity is short, and we should just collapse it. 607 content = ''.join([chunk for chunk in cherrypy.response.body])608 cherrypy.response.body = [content]617 content = ''.join([chunk for chunk in response.body]) 618 response.body = [content] 609 619 l = len(content) 610 620 if l and l < s: 611 621 # IN ADDITION: the response must be written to IE 612 622 # in one chunk or it will still get replaced! Bah. 613 cherrypy.response.body = [cherrypy.response.body[0] + (" " * (s - l))]614 cherrypy.response.headerMap['Content-Length'] = s623 response.body = [response.body[0] + (" " * (s - l))] 624 response.headerMap['Content-Length'] = s 615 625 616 626 # Headers 617 627 headers = [] 618 for key, valueList in cherrypy.response.headerMap.iteritems():628 for key, valueList in response.headerMap.iteritems(): 619 629 order = _header_order_map.get(key, 3) 620 630 if not isinstance(valueList, list): … … 626 636 # ending with the entity-header fields.' 627 637 headers.sort() 628 cherrypy.response.headers = [item[1] for item in headers]629 630 cookie = cherrypy.response.simpleCookie.output()638 response.headers = [item[1] for item in headers] 639 640 cookie = response.simpleCookie.output() 631 641 if cookie: 632 642 lines = cookie.split("\n") 633 643 for line in lines: 634 644 name, value = line.split(": ", 1) 635 cherrypy.response.headers.append((name, value))645 response.headers.append((name, value)) 636 646 637 647 trunk/cherrypy/lib/cptools.py
r639 r679 270 270 cherrypy.log(" Found file: %s" % path, "DEBUG") 271 271 272 response.headerMap["Accept-Ranges"] = "bytes" 273 r = getRanges(c_len) 274 if r: 275 if len(r) == 1: 276 # Return a single-part response. 277 start, stop = r[0] 278 r_len = stop - start 279 response.status = "206 Partial Content" 280 response.headerMap['Content-Range'] = ("bytes %s-%s/%s" % 281 (start, stop - 1, c_len)) 282 response.headerMap['Content-Length'] = r_len 283 bodyfile.seek(start) 284 response.body = [bodyfile.read(r_len)] 272 # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code 273 if cherrypy.response.version >= "1.1": 274 response.headerMap["Accept-Ranges"] = "bytes" 275 r = getRanges(c_len) 276 if r: 277 if len(r) == 1: 278 # Return a single-part response. 279 start, stop = r[0] 280 r_len = stop - start 281 response.status = "206 Partial Content" 282 response.headerMap['Content-Range'] = ("bytes %s-%s/%s" % 283 (start, stop - 1, c_len)) 284 response.headerMap['Content-Length'] = r_len 285 bodyfile.seek(start) 286 response.body = [bodyfile.read(r_len)] 287 else: 288 # Return a multipart/byteranges response. 289 response.status = "206 Partial Content" 290 boundary = mimetools.choose_boundary() 291 ct = "multipart/byteranges; boundary=%s" % boundary 292 response.headerMap['Content-Type'] = ct 293 ## del response.headerMap['Content-Length'] 294 295 def fileRanges(): 296 for start, stop in r: 297 yield "--" + boundary 298 yield "\nContent-type: %s" % contentType 299 yield ("\nContent-range: bytes %s-%s/%s\n\n" 300 % (start, stop - 1, c_len)) 301 bodyfile.seek(start) 302 yield bodyfile.read((stop + 1) - start) 303 yield "\n" 304 # Final boundary 305 yield "--" + boundary 306 response.body = fileRanges() 285 307 else: 286 # Return a multipart/byteranges response. 287 response.status = "206 Partial Content" 288 boundary = mimetools.choose_boundary() 289 ct = "multipart/byteranges; boundary=%s" % boundary 290 response.headerMap['Content-Type'] = ct 291 del response.headerMap['Content-Length'] 292 293 def fileRanges(): 294 for start, stop in r: 295 yield "--" + boundary 296 yield "\nContent-type: %s" % contentType 297 yield ("\nContent-range: bytes %s-%s/%s\n\n" 298 % (start, stop - 1, c_len)) 299 bodyfile.seek(start) 300 yield bodyfile.read((stop + 1) - start) 301 yield "\n" 302 # Final boundary 303 yield "--" + boundary 304 response.body = fileRanges() 305 308 response.headerMap['Content-Length'] = c_len 309 response.body = fileGenerator(bodyfile) 306 310 else: 307 311 response.headerMap['Content-Length'] = c_len trunk/cherrypy/test/helper.py
r665 r679 120 120 self.body.append(chunk) 121 121 except: 122 if cherrypy.config.get("s erver.protocolVersion") == "HTTP/1.0":122 if cherrypy.config.get("streamResponse", False): 123 123 # Pass the error through 124 124 raise trunk/cherrypy/test/test.py
r660 r679 252 252 253 253 import cherrypy 254 print "Python version used to run this test script:", sys.version.split()[0] 254 v = sys.version.split()[0] 255 print "Python version used to run this test script:", v 255 256 print "CherryPy version", cherrypy.__version__ 256 257 print trunk/cherrypy/test/test_core.py
r665 r679 169 169 raise ValueError() 170 170 171 def page_http_1_1(self): 172 cherrypy.response.headerMap["Content-Length"] = 39 173 def inner(): 174 yield "hello" 175 raise ValueError() 176 yield "very oops" 177 return inner() 171 def page_streamed(self): 172 yield "hello" 173 raise ValueError() 174 yield "very oops" 178 175 179 176 def cause_err_in_finalize(self): … … 296 293 'server.logTracebacks': True, 297 294 }, 295 '/error/page_streamed': { 296 'streamResponse': True, 297 }, 298 298 }) 299 299 … … 507 507 self.getPage("/error/page_method") 508 508 self.assertErrorPage(500, valerr) 509 509 510 self.getPage("/error/page_yield") 511 self.assertErrorPage(500, valerr) 512 510 513 import cherrypy 511 proto = cherrypy.config.get("server.protocolVersion", "HTTP/1.0") 512 if proto == "HTTP/1.1": 513 valerr = r'Unrecoverable error in the server.$' 514 self.getPage("/error/page_yield") 515 self.assertMatchesBody(valerr) 516 517 if cherrypy._httpserver is None and proto == "HTTP/1.0": 518 self.assertRaises(ValueError, self.getPage, "/error/page_http_1_1") 514 # streamResponse should be True for this path. 515 if cherrypy._httpserver is None: 516 self.assertRaises(ValueError, self.getPage, 517 "/error/page_streamed") 519 518 else: 520 self.getPage("/error/page_ http_1_1")519 self.getPage("/error/page_streamed") 521 520 # Because this error is raised after the response body has 522 521 # started, the status should not change to an error status. … … 568 567 --%s""" % (boundary, boundary, boundary) 569 568 self.assertBody(expected_body) 570 self.assert NoHeader("Content-Length")569 self.assertHeader("Content-Length") 571 570 572 571 # Test "416 Requested Range Not Satisfiable" trunk/cherrypy/test/test_gzip_filter.py
r639 r679 41 41 yield "Here be dragons" 42 42 noshow.exposed = True 43 44 def noshow_stream(self): 45 # Test for ticket #147, where yield showed no exceptions (content- 46 # encoding was still gzip even though traceback wasn't zipped). 47 raise IndexError() 48 yield "Here be dragons" 49 noshow_stream.exposed = True 43 50 44 51 cherrypy.root = Root() 45 52 cherrypy.config.update({ 46 'server.logToScreen': False, 47 'server.environment': 'production', 48 'server.showTracebacks': True, 49 'gzipFilter.on': True, 53 'global': {'server.logToScreen': False, 54 'server.environment': 'production', 55 'server.showTracebacks': True, 56 'gzipFilter.on': True, 57 }, 58 '/noshow_stream': {'streamResponse': True}, 50 59 }) 51 60 … … 70 79 try: 71 80 self.getPage('/noshow', headers=[("Accept-Encoding", "gzip")]) 81 self.assertNoHeader('Content-Encoding') 82 self.assertStatus('500 Internal error') 83 self.assertErrorPage(500, "IndexError\n") 72 84 73 import cherrypy 74 proto = cherrypy.config.get("server.protocolVersion", "HTTP/1.0") 75 if proto == "HTTP/1.1": 76 # In this case, there's nothing we can do to deliver a 77 # readable page, since 1) the gzip header is already set, 78 # and 2) we may have already written some of the body. 79 # The fix is to not use yield when using HTTP/1.1 and gzip. 85 # In this case, there's nothing we can do to deliver a 86 # readable page, since 1) the gzip header is already set, 87 # and 2) we may have already written some of the body. 88 # The fix is to never stream yields when using gzip. 89 if cherrypy._httpserver is None: 90 self.assertRaises(IndexError, self.getPage, 91 '/noshow_stream', 92 [("Accept-Encoding", "gzip")]) 93 else: 94 self.getPage('/noshow_stream', 95 headers=[("Accept-Encoding", "gzip")]) 80 96 self.assertHeader('Content-Encoding', 'gzip') 81 97 self.assertMatchesBody(r"Unrecoverable error in the server.$") 82 else:83 self.assertNoHeader('Content-Encoding')84 self.assertStatus('500 Internal error')85 self.assertErrorPage(500, "IndexError\n")86 98 finally: 87 99 helper.webtest.ignored_exceptions.pop()

