Changeset 1263
- Timestamp:
- 08/21/06 16:06:43
- Files:
-
- trunk/cherrypy/_cpengine.py (modified) (2 diffs)
- trunk/cherrypy/_cprequest.py (modified) (7 diffs)
- trunk/cherrypy/_cpwsgi.py (modified) (1 diff)
- trunk/cherrypy/_cpwsgiserver.py (modified) (5 diffs)
- trunk/cherrypy/test/test.py (modified) (1 diff)
- trunk/cherrypy/test/test_caching.py (modified) (4 diffs)
- trunk/cherrypy/test/test_conn.py (modified) (4 diffs)
- trunk/cherrypy/test/test_core.py (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cpengine.py
r1246 r1263 190 190 " receive requests, False otherwise.") 191 191 192 def request(self, local_host, remote_host, scheme="http"): 192 def request(self, local_host, remote_host, scheme="http", 193 server_protocol="HTTP/1.1"): 193 194 """Obtain an HTTP Request object. 194 195 … … 210 211 for func in self.on_start_thread_list: 211 212 func(i) 212 req = self.request_class(local_host, remote_host, scheme) 213 req = self.request_class(local_host, remote_host, scheme, 214 server_protocol) 213 215 cherrypy.serving.request = req 214 216 cherrypy.serving.response = resp = self.response_class() trunk/cherrypy/_cprequest.py
r1261 r1263 82 82 remote = http.Host("localhost", 1111) 83 83 scheme = "http" 84 server_protocol = "HTTP/1.1" 84 85 base = "" 85 86 … … 115 116 hooks = HookMap(hookpoints) 116 117 117 def __init__(self, local_host, remote_host, scheme="http"): 118 def __init__(self, local_host, remote_host, scheme="http", 119 server_protocol="HTTP/1.1"): 118 120 """Populate a new Request object. 119 121 … … 125 127 self.remote = remote_host 126 128 self.scheme = scheme 129 self.server_protocol = server_protocol 127 130 128 131 self.closed = False … … 142 145 cherrypy.serving.__dict__.clear() 143 146 144 def run(self, method, path, query_string, protocol, headers, rfile):147 def run(self, method, path, query_string, req_protocol, headers, rfile): 145 148 """Process the Request. 146 149 147 method, path, query_string, and protocol should be pulled directly150 method, path, query_string, and req_protocol should be pulled directly 148 151 from the Request-Line (e.g. "GET /path?key=val HTTP/1.0"). 149 152 path should be %XX-unquoted, but query_string should not be. … … 160 163 161 164 """ 165 162 166 try: 163 167 self.error_response = cherrypy.HTTPError(500).set_response … … 166 170 self.path = path or "/" 167 171 self.query_string = query_string 168 self.protocol = int(protocol[5]), int(protocol[7]) 172 173 # Compare request and server HTTP protocol versions, in case our 174 # server does not support the requested protocol. Limit our output 175 # to min(req, server). We want the following output: 176 # request server actual written supported response 177 # protocol protocol response protocol feature set 178 # a 1.0 1.0 1.0 1.0 179 # b 1.0 1.1 1.1 1.0 180 # c 1.1 1.0 1.0 1.0 181 # d 1.1 1.1 1.1 1.1 182 # Notice that, in (b), the response will be "HTTP/1.1" even though 183 # the client only understands 1.0. RFC 2616 10.5.6 says we should 184 # only return 505 if the _major_ version is different. 185 rp = int(req_protocol[5]), int(req_protocol[7]) 186 sp = int(self.server_protocol[5]), int(self.server_protocol[7]) 187 self.protocol = min(rp, sp) 169 188 170 189 # Rebuild first line of the request (e.g. "GET /path HTTP/1.0"). … … 172 191 if query_string: 173 192 url += '?' + query_string 174 self.request_line = '%s %s %s' % (method, url, protocol)193 self.request_line = '%s %s %s' % (method, url, req_protocol) 175 194 176 195 self.header_list = list(headers) trunk/cherrypy/_cpwsgi.py
r1260 r1263 36 36 int(env('REMOTE_PORT', -1)), 37 37 env('REMOTE_HOST', '')) 38 request = cherrypy.engine.request(local, remote, env('wsgi.url_scheme')) 38 request = cherrypy.engine.request(local, remote, 39 env('wsgi.url_scheme'), 40 env('ACTUAL_SERVER_PROTOCOL', None)) 39 41 40 42 # LOGON_USER is served by IIS, and is the name of the trunk/cherrypy/_cpwsgiserver.py
r1259 r1263 111 111 # to min(req, server). We want the following output: 112 112 # request server actual written supported response 113 # protocol protocol response protocol feature set (SERVER_PROTOCOL)113 # protocol protocol response protocol feature set 114 114 # a 1.0 1.0 1.0 1.0 115 115 # b 1.0 1.1 1.1 1.0 … … 124 124 self.simple_response("505 HTTP Version Not Supported") 125 125 return 126 self.environ["SERVER_PROTOCOL"] = "HTTP/%s.%s" % min(rp, sp) 126 # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol. 127 self.environ["SERVER_PROTOCOL"] = req_protocol 128 # set a non-standard environ entry so the WSGI app can know what 129 # the *real* server protocol is (and what features to support). 130 self.environ["ACTUAL_SERVER_PROTOCOL"] = server.protocol 131 self.response_protocol = "HTTP/%s.%s" % min(rp, sp) 127 132 128 133 # If the Request-URI was an absoluteURI, use its location atom. … … 145 150 146 151 # Persistent connection support 147 if self. environ["SERVER_PROTOCOL"]== "HTTP/1.1":152 if self.response_protocol == "HTTP/1.1": 148 153 if headers.getheader("Connection", "") == "close": 149 154 self.close_connection = True … … 247 252 wfile.write("Content-Length: %s\r\n" % len(msg)) 248 253 249 if status[:3] == "413" and self. environ["SERVER_PROTOCOL"]== 'HTTP/1.1':254 if status[:3] == "413" and self.response_protocol == 'HTTP/1.1': 250 255 # Request Entity Too Large 251 256 self.close_connection = True … … 281 286 hkeys = [key.lower() for (key,value) in self.outheaders] 282 287 283 if (self. environ["SERVER_PROTOCOL"]== 'HTTP/1.1'288 if (self.response_protocol == 'HTTP/1.1' 284 289 and (# Request Entity Too Large. Close conn to avoid garbage. 285 290 self.status[:3] == "413" trunk/cherrypy/test/test.py
r1262 r1263 36 36 print "Python version used to run this test script:", v 37 37 print "CherryPy version", cherrypy.__version__ 38 print "HTTP server version", self.protocol 38 39 print 39 40 trunk/cherrypy/test/test_caching.py
r1215 r1263 125 125 # This also gives us a chance to test 0 expiry with no other headers 126 126 self.assertHeader("Pragma", "no-cache") 127 self.assertHeader("Cache-Control", "no-cache") 127 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 128 self.assertHeader("Cache-Control", "no-cache") 128 129 d = self.assertHeader("Date") 129 130 self.assertHeader("Expires", d) … … 133 134 self.assertStatus("200 OK") 134 135 self.assertHeader("Pragma", "no-cache") 135 self.assertHeader("Cache-Control", "no-cache") 136 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 137 self.assertHeader("Cache-Control", "no-cache") 136 138 d = self.assertHeader("Date") 137 139 self.assertHeader("Expires", d) … … 141 143 self.assertStatus("200 OK") 142 144 self.assertHeader("Pragma", "no-cache") 143 self.assertHeader("Cache-Control", "no-cache") 145 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 146 self.assertHeader("Cache-Control", "no-cache") 144 147 d = self.assertHeader("Date") 145 148 self.assertHeader("Expires", d) … … 150 153 # overwritten here ... 151 154 self.assertHeader("Pragma", "no-cache") 152 self.assertHeader("Cache-Control", "no-cache") 155 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 156 self.assertHeader("Cache-Control", "no-cache") 153 157 d = self.assertHeader("Date") 154 158 self.assertHeader("Expires", d) trunk/cherrypy/test/test_conn.py
r1259 r1263 51 51 52 52 def test_HTTP11(self): 53 if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": 54 print "skipped ", 55 return 56 53 57 self.PROTOCOL = "HTTP/1.1" 54 58 … … 89 93 90 94 def test_HTTP11_pipelining(self): 95 if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": 96 print "skipped ", 97 return 98 91 99 self.PROTOCOL = "HTTP/1.1" 92 100 … … 123 131 124 132 def test_100_Continue(self): 133 if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": 134 print "skipped ", 135 return 136 125 137 self.PROTOCOL = "HTTP/1.1" 126 138 … … 171 183 172 184 def test_Chunked_Encoding(self): 185 if cherrypy.config.get('server.protocol_version') != "HTTP/1.1": 186 print "skipped ", 187 return 188 173 189 self.PROTOCOL = "HTTP/1.1" 174 190 trunk/cherrypy/test/test_core.py
r1259 r1263 373 373 'log_to_screen': False, 374 374 'log_file': log_file, 375 'server.protocol_version': "HTTP/1.1",376 375 'environment': 'production', 377 376 'show_tracebacks': True, … … 469 468 line = data[-2].strip() 470 469 if haslength: 471 self.assert_(line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 7 "" ""' 472 % self.prefix())) 470 if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 7 "" ""' 471 % self.prefix()): 472 self.fail(line) 473 473 else: 474 self.assert_(line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 - "" ""' 475 % self.prefix())) 474 if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 - "" ""' 475 % self.prefix()): 476 self.fail(line) 476 477 477 478 self.assertEqual(data[-1][:15], '127.0.0.1 - - [') … … 586 587 self.assertStatus(200) 587 588 self.assertBody("(['http://127.0.0.1:%s/'], 302)" % self.PORT) 588 self.getPage("/redirect/stringify", protocol="HTTP/1.1") 589 self.assertStatus(200) 590 self.assertBody("(['http://127.0.0.1:%s/'], 303)" % self.PORT) 589 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 590 self.getPage("/redirect/stringify", protocol="HTTP/1.1") 591 self.assertStatus(200) 592 self.assertBody("(['http://127.0.0.1:%s/'], 303)" % self.PORT) 591 593 592 594 def testFlatten(self): … … 653 655 654 656 # Get a partial file. 655 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) 656 self.assertStatus(206) 657 self.assertHeader("Content-Type", "text/html") 658 self.assertHeader("Content-Range", "bytes 2-5/14") 659 self.assertBody("llo,") 660 661 # What happens with overlapping ranges (and out of order, too)? 662 self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')]) 663 self.assertStatus(206) 664 ct = "" 665 for k, v in self.headers: 666 if k.lower() == "content-type": 667 ct = v 668 break 669 expected_type = "multipart/byteranges; boundary=" 670 self.assert_(ct.startswith(expected_type)) 671 boundary = ct[len(expected_type):] 672 expected_body = """--%s 657 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 658 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) 659 self.assertStatus(206) 660 self.assertHeader("Content-Type", "text/html") 661 self.assertHeader("Content-Range", "bytes 2-5/14") 662 self.assertBody("llo,") 663 664 # What happens with overlapping ranges (and out of order, too)? 665 self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')]) 666 self.assertStatus(206) 667 ct = "" 668 for k, v in self.headers: 669 if k.lower() == "content-type": 670 ct = v 671 break 672 expected_type = "multipart/byteranges; boundary=" 673 self.assert_(ct.startswith(expected_type)) 674 boundary = ct[len(expected_type):] 675 expected_body = """--%s 673 676 Content-type: text/html 674 677 Content-range: bytes 4-6/14 … … 681 684 llo, 682 685 --%s""" % (boundary, boundary, boundary) 683 self.assertBody(expected_body)684 self.assertHeader("Content-Length")685 686 # Test "416 Requested Range Not Satisfiable"687 self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')])688 self.assertStatus(416)689 self.assertHeader("Content-Range", "bytes */14")690 691 # Test Range behavior with HTTP/1.0 request692 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')], protocol="HTTP/1.0")693 self.assertStatus(200)694 self.assertBody("Hello, world\r\n")686 self.assertBody(expected_body) 687 self.assertHeader("Content-Length") 688 689 # Test "416 Requested Range Not Satisfiable" 690 self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')]) 691 self.assertStatus(416) 692 self.assertHeader("Content-Range", "bytes */14") 693 elif cherrypy.config.get('server.protocol_version') == "HTTP/1.0": 694 # Test Range behavior with HTTP/1.0 request 695 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')]) 696 self.assertStatus(200) 697 self.assertBody("Hello, world\r\n") 695 698 696 699 def testExpect(self): … … 759 762 self.assertEqual(hnames.count(key), 1) 760 763 761 # Test RFC-2047-encoded request and response header values 762 self.getPage("/headers/ifmatch", 763 [('If-Match', '=?utf-8?q?=E2=84=ABngstr=C3=B6m?=')]) 764 self.assertBody("u'\\u212bngstr\\xf6m'") 765 self.assertHeader("ETag", '=?utf-8?b?4oSrbmdzdHLDtm0=?=') 766 767 # Test a *LONG* RFC-2047-encoded request and response header value 768 c = "=E2=84=ABngstr=C3=B6m" 769 self.getPage("/headers/ifmatch", 770 [('If-Match', '=?utf-8?q?%s?=' % (c * 10))]) 771 self.assertBody("u'%s'" % ('\\u212bngstr\\xf6m' * 10)) 772 self.assertHeader("ETag", 773 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?=' 774 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?=' 775 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2bQ==?=') 764 if cherrypy.config.get('server.protocol_version') == "HTTP/1.1": 765 # Test RFC-2047-encoded request and response header values 766 self.getPage("/headers/ifmatch", 767 [('If-Match', '=?utf-8?q?=E2=84=ABngstr=C3=B6m?=')]) 768 self.assertBody("u'\\u212bngstr\\xf6m'") 769 self.assertHeader("ETag", '=?utf-8?b?4oSrbmdzdHLDtm0=?=') 770 771 # Test a *LONG* RFC-2047-encoded request and response header value 772 c = "=E2=84=ABngstr=C3=B6m" 773 self.getPage("/headers/ifmatch", 774 [('If-Match', '=?utf-8?q?%s?=' % (c * 10))]) 775 self.assertBody("u'%s'" % ('\\u212bngstr\\xf6m' * 10)) 776 self.assertHeader("ETag", 777 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?=' 778 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?=' 779 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2bQ==?=') 776 780 777 781 # Test that two request headers are collapsed into one.

