Changeset 1224
- Timestamp:
- 08/06/06 20:09:48
- Files:
-
- trunk/cherrypy/_cpengine.py (modified) (1 diff)
- trunk/cherrypy/_cperror.py (modified) (1 diff)
- trunk/cherrypy/_cpmodpy.py (modified) (1 diff)
- trunk/cherrypy/_cprequest.py (modified) (8 diffs)
- trunk/cherrypy/_cpwsgi.py (modified) (5 diffs)
- trunk/cherrypy/_cpwsgiserver.py (modified) (7 diffs)
- trunk/cherrypy/lib/caching.py (modified) (1 diff)
- trunk/cherrypy/lib/http.py (modified) (5 diffs)
- trunk/cherrypy/lib/static.py (modified) (1 diff)
- trunk/cherrypy/lib/wsgiapp.py (modified) (2 diffs)
- trunk/cherrypy/test/benchmark.py (modified) (1 diff)
- trunk/cherrypy/test/test_objectmapping.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cpengine.py
r1220 r1224 229 229 pass 230 230 231 def run(self, request_line, headers, rfile):231 def run(self, method, path, query_string, protocol, headers, rfile): 232 232 self.method = "GET" 233 233 cherrypy.HTTPError(503, self.msg).set_response() trunk/cherrypy/_cperror.py
r1209 r1224 73 73 # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html 74 74 if status is None: 75 if cherrypy.re sponse.version>= (1, 1):75 if cherrypy.request.protocol >= (1, 1): 76 76 status = 303 77 77 else: trunk/cherrypy/_cpmodpy.py
r1158 r1224 83 83 84 84 # Run the CherryPy Request object and obtain the response 85 requestLine = req.the_request86 85 headers = req.headers_in.items() 87 86 rfile = _ReadOnlyRequest(req) 88 response = request.run(requestLine, headers, rfile) 87 response = request.run(req.method, req.uri, req.args or "", 88 req.protocol, headers, rfile) 89 89 90 90 sendResponse(req, response.status, response.header_list, response.body) trunk/cherrypy/_cprequest.py
r1223 r1224 65 65 path = "" 66 66 query_string = "" 67 protocol = ""67 protocol = (1, 1) 68 68 params = {} 69 version = http.version_from_http("HTTP/1.1")70 69 71 70 # Message attributes … … 116 115 cherrypy.serving.__dict__.clear() 117 116 118 def run(self, request_line, headers, rfile):117 def run(self, method, path, query_string, protocol, headers, rfile): 119 118 """Process the Request. 120 119 121 request_line should be of the form "GET /path HTTP/1.0". 120 method, path, query_string, and protocol should be pulled directly 121 from the Request-Line (e.g. "GET /path?key=val HTTP/1.0"). 122 path should be %XX-unquoted, but query_string should not be. 122 123 headers should be a list of (name, value) tuples. 123 124 rfile should be a file-like object containing the HTTP request entity. … … 132 133 133 134 """ 134 self.error_response = cherrypy.HTTPError(500).set_response135 136 self.request_line = request_line.strip()137 self.header_list = list(headers)138 self.rfile = rfile139 self.headers = http.HeaderMap()140 self.simple_cookie = Cookie.SimpleCookie()141 self.handler = None142 143 135 try: 144 self.process_request_line() 136 self.error_response = cherrypy.HTTPError(500).set_response 137 138 self.method = method 139 self.path = path or "/" 140 self.query_string = query_string 141 self.protocol = int(protocol[5]), int(protocol[7]) 142 143 # Rebuild first line of the request (e.g. "GET /path HTTP/1.0"). 144 url = path 145 if query_string: 146 url += '?' + query_string 147 self.request_line = '%s %s %s' % (method, url, protocol) 148 149 self.header_list = list(headers) 150 self.rfile = rfile 151 self.headers = http.HeaderMap() 152 self.simple_cookie = Cookie.SimpleCookie() 153 self.handler = None 145 154 146 155 # Get the 'Host' header, so we can do HTTPRedirects properly. … … 154 163 raise cherrypy.NotFound() 155 164 self.app = cherrypy.tree.apps[r] 165 else: 166 self.script_name = self.app.script_name 156 167 157 168 # path_info should be the path from the … … 228 239 self.hooks.run('on_end_resource') 229 240 230 def process_request_line(self):231 """Parse the first line (e.g. "GET /path HTTP/1.1") of the request."""232 rl = self.request_line233 method, path, qs, proto = http.parse_request_line(rl)234 if path == "*":235 path = "global"236 237 self.method = method238 self.path = path239 self.query_string = qs240 self.protocol = proto241 242 # Compare request and server HTTP versions, in case our server does243 # not support the requested version. We can't tell the server what244 # version number to write in the response, so we limit our output245 # to min(req, server). We want the following output:246 # request server actual written supported response247 # version version response version feature set (resp.v)248 # a 1.0 1.0 1.0 1.0249 # b 1.0 1.1 1.1 1.0250 # c 1.1 1.0 1.0 1.0251 # d 1.1 1.1 1.1 1.1252 # Notice that, in (b), the response will be "HTTP/1.1" even though253 # the client only understands 1.0. RFC 2616 10.5.6 says we should254 # only return 505 if the _major_ version is different.255 256 # cherrypy.request.version == request.protocol in a Version instance.257 self.version = http.version_from_http(self.protocol)258 259 # cherrypy.response.version should be used to determine whether or260 # not to include a given HTTP/1.1 feature in the response content.261 server_v = cherrypy.config.get('server.protocol_version', 'HTTP/1.0')262 server_v = http.version_from_http(server_v)263 cherrypy.response.version = min(self.version, server_v)264 265 241 def process_headers(self): 266 242 self.params = http.parseQueryString(self.query_string) … … 291 267 # (Bad Request) status code to any HTTP/1.1 request message 292 268 # which lacks a Host header field. 293 if self. version>= (1, 1):269 if self.protocol >= (1, 1): 294 270 msg = "HTTP/1.1 requires a 'Host' request header." 295 271 raise cherrypy.HTTPError(400, msg) … … 642 618 simple_cookie = Cookie.SimpleCookie() 643 619 body = Body() 644 version = (1, 0)645 620 646 621 def __init__(self): … … 693 668 694 669 # Transform our header dict into a sorted list of tuples. 695 self.header_list = h = headers.output( self.version)670 self.header_list = h = headers.output(cherrypy.request.protocol) 696 671 697 672 cookie = self.simple_cookie.output() trunk/cherrypy/_cpwsgi.py
r1197 r1224 7 7 from cherrypy.lib import http 8 8 9 10 def request_line(environ):11 """Rebuild first line of the request (e.g. "GET /path HTTP/1.0")."""12 13 resource = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '')14 if not (resource == "*" or resource.startswith("/")):15 resource = "/" + resource16 17 qString = environ.get('QUERY_STRING')18 if qString:19 resource += '?' + qString20 21 resource = resource.replace(" ", "%20")22 23 return ('%s %s %s' % (environ['REQUEST_METHOD'],24 resource or '/',25 environ['SERVER_PROTOCOL']26 )27 )28 9 29 10 headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization', … … 57 38 # user after having been mapped to a local account. 58 39 # Both IIS and Apache set REMOTE_USER, when possible. 59 request.login = (env('LOGON_USER') or env('REMOTE_USER') or None)40 request.login = env('LOGON_USER') or env('REMOTE_USER') or None 60 41 61 42 request.multithread = environ['wsgi.multithread'] … … 65 46 if app: 66 47 request.app = app 67 request.script_name = app.script_name68 48 69 response = request.run(request_line(environ), 49 path = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '') 50 response = request.run(environ['REQUEST_METHOD'], path, 51 environ.get('QUERY_STRING'), 52 environ.get('SERVER_PROTOCOL'), 70 53 translate_headers(environ), 71 54 environ['wsgi.input']) … … 161 144 except http.MaxSizeExceeded: 162 145 msg = "Request Entity Too Large" 163 proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0") 164 self.wfile.write("%s 413 %s\r\n" % (proto, msg)) 146 self.wfile.write("%s 413 %s\r\n" % (self.server.protocol, msg)) 165 147 self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) 166 148 self.wfile.write(msg) … … 214 196 request_queue_size = conf('server.socket_queue_size'), 215 197 ) 198 s.protocol = conf('server.protocol_version', 'HTTP/1.0') 216 199 trunk/cherrypy/_cpwsgiserver.py
r1218 r1224 1 1 """A high-speed, production ready, thread pooled, generic WSGI server.""" 2 2 3 import mimetools # todo: use email 4 import Queue 5 import re 6 quoted_slash = re.compile("(?i)%2F") 7 import rfc822 3 8 import socket 9 import sys 4 10 import threading 5 import Queue6 import mimetools # todo: use email7 import rfc8228 import sys9 11 import time 10 12 import traceback 13 from urllib import unquote 14 from urlparse import urlparse 11 15 12 16 import errno … … 64 68 self.ready = False 65 69 return 66 method,path,version = request_line.strip().split(" ", 2) 67 if "?" in path: 68 path, qs = path.split("?", 1) 69 else: 70 qs = "" 70 71 method, path, req_protocol = request_line.strip().split(" ", 2) 71 72 self.environ["REQUEST_METHOD"] = method 73 74 # path may be an abs_path (including "http://host.domain.tld"); 75 scheme, location, path, params, qs, frag = urlparse(path) 76 if params: 77 path = path + ";" + params 78 79 # Unquote the path+params (e.g. "/this%20path" -> "this path"). 80 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 81 # 82 # But note that "...a URI must be separated into its components 83 # before the escaped characters within those components can be 84 # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2 85 atoms = [unquote(x) for x in quoted_slash.split(path)] 86 path = "%2F".join(atoms) 72 87 73 88 for mount_point, wsgi_app in self.server.mount_points: … … 89 104 return 90 105 106 # Note that, like wsgiref and most other WSGI servers, 107 # we unquote the path but not the query string. 91 108 self.environ["QUERY_STRING"] = qs 92 self.environ["SERVER_PROTOCOL"] = version 93 self.environ["SERVER_NAME"] = self.server.server_name 109 110 # Compare request and server HTTP protocol versions, in case our 111 # server does not support the requested protocol. Limit our output 112 # to min(req, server). We want the following output: 113 # request server actual written supported response 114 # protocol protocol response protocol feature set (SERVER_PROTOCOL) 115 # a 1.0 1.0 1.0 1.0 116 # b 1.0 1.1 1.1 1.0 117 # c 1.1 1.0 1.0 1.0 118 # d 1.1 1.1 1.1 1.1 119 # Notice that, in (b), the response will be "HTTP/1.1" even though 120 # the client only understands 1.0. RFC 2616 10.5.6 says we should 121 # only return 505 if the _major_ version is different. 122 rp = int(req_protocol[5]), int(req_protocol[7]) 123 sp = int(self.server.protocol[5]), int(self.server.protocol[7]) 124 if sp[0] != rp[0]: 125 self.abort("505 HTTP Version Not Supported") 126 return 127 self.environ["SERVER_PROTOCOL"] = "HTTP/%s.%s" % min(rp, sp) 128 129 # If the Request-URI was an absoluteURI, use its location atom. 130 self.environ["SERVER_NAME"] = location or self.server.server_name 131 94 132 if isinstance(self.server.bind_addr, basestring): 95 133 # AF_UNIX. This isn't really allowed by WSGI, which doesn't … … 102 140 self.environ["REMOTE_ADDR"] = self.addr[0] 103 141 self.environ["REMOTE_PORT"] = str(self.addr[1]) 142 104 143 # then all the http headers 105 144 headers = mimetools.Message(self.rfile) … … 126 165 def abort(self, status, msg=""): 127 166 """Write a simple error message back to the client.""" 128 proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0") 129 self.wfile.write("%s %s\r\n" % (proto, status)) 167 self.wfile.write("%s %s\r\n" % (self.server.protocol, status)) 130 168 self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) 131 169 if msg: … … 163 201 if "server" not in self.outheaderkeys: 164 202 self.outheaders.append(("Server", self.server.version)) 165 if (self. environ["SERVER_PROTOCOL"]== "HTTP/1.1"203 if (self.server.protocol == "HTTP/1.1" 166 204 and "connection" not in self.outheaderkeys): 167 205 self.outheaders.append(("Connection", "close")) 168 self.wfile.write(self. environ["SERVER_PROTOCOL"]+ " " + self.status + "\r\n")206 self.wfile.write(self.server.protocol + " " + self.status + "\r\n") 169 207 for (k,v) in self.outheaders: 170 208 self.wfile.write(k + ": " + v + "\r\n") … … 238 276 """ 239 277 278 protocol = "HTTP/1.0" 240 279 version = "CherryPy/3.0.0alpha" 241 280 ready = False trunk/cherrypy/lib/caching.py
r1222 r1224 198 198 if force or "Pragma" not in cherrypy.response.headers: 199 199 cherrypy.response.headers["Pragma"] = "no-cache" 200 if cherrypy.request. version>= (1, 1):200 if cherrypy.request.protocol >= (1, 1): 201 201 if force or "Cache-Control" not in cherrypy.response.headers: 202 202 cherrypy.response.headers["Cache-Control"] = "no-cache" trunk/cherrypy/lib/http.py
r1200 r1224 26 26 HTTPDate = rfc822.formatdate 27 27 import time 28 from urllib import unquote29 from urlparse import urlparse30 28 31 29 … … 36 34 return url 37 35 38 def version_from_http(version_str):39 """Return a Versiontuple from the given 'HTTP/x.y' string."""40 return int( version_str[5]), int(version_str[7])36 def protocol_from_http(protocol_str): 37 """Return a protocol tuple from the given 'HTTP/x.y' string.""" 38 return int(protocol_str[5]), int(protocol_str[7]) 41 39 42 40 def getRanges(headervalue, content_length): … … 240 238 241 239 242 quoted_slash = re.compile("(?i)%2F")243 244 def parse_request_line(request_line):245 """Return (method, path, querystring, protocol) from a request_line."""246 method, path, protocol = request_line.split()247 248 # path may be an abs_path (including "http://host.domain.tld");249 # Ignore scheme, location, and fragments (so config lookups work).250 # [Therefore, this assumes all hosts are valid for this server.251 # Note that we are also violating the RFC which says: if the host252 # given is an abs_path, it must override any Host header.]253 scheme, location, path, params, qs, frag = urlparse(path)254 255 if params:256 path = path + ";" + params257 258 # Unquote the path (e.g. "/this%20path" -> "this path").259 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2260 #261 # But note that "...a URI must be separated into its components262 # before the escaped characters within those components can be263 # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2264 #265 # Note also that cgi.parse_qs will decode the querystring for us.266 atoms = [unquote(x) for x in quoted_slash.split(path)]267 path = "%2F".join(atoms)268 269 return method, path, qs, protocol270 271 272 240 image_map_pattern = re.compile(r"[0-9]+,[0-9]+") 273 241 … … 369 337 return header_elements(key, h) 370 338 371 def output(self, version=(1, 1)):339 def output(self, protocol=(1, 1)): 372 340 """Transform self into a list of (name, value) tuples.""" 373 341 header_list = [] … … 386 354 # outside the US-ASCII character set may assume that 387 355 # they represent ISO-8859-1 characters." 388 if version>= (1, 1):356 if protocol >= (1, 1): 389 357 v = v.encode("utf-8") 390 358 v = Header(v, 'utf-8').encode() trunk/cherrypy/lib/static.py
r1221 r1224 73 73 74 74 # HTTP/1.0 didn't have Range/Accept-Ranges headers, or the 206 code 75 if cherrypy.re sponse.version>= (1, 1):75 if cherrypy.request.protocol >= (1, 1): 76 76 response.headers["Accept-Ranges"] = "bytes" 77 77 r = http.getRanges(cherrypy.request.headers.get('Range'), c_len) trunk/cherrypy/lib/wsgiapp.py
r1217 r1224 32 32 environ["QUERY_STRING"] = cherrypy.request.query_string 33 33 environ["SERVER_PROTOCOL"] = cherrypy.request.protocol 34 environ["SERVER_NAME"] = cherrypy.request.wsgi_environ['SERVER_NAME'] 35 environ["SERVER_PORT"] = cherrypy.request.wsgi_environ['SERVER_PORT'] 34 server_name = getattr(cherrypy.server.httpserver, 'server_name', "None") 35 environ["SERVER_NAME"] = server_name 36 environ["SERVER_PORT"] = cherrypy.config.get('server.socket_port') 36 37 environ["REMOTE_HOST"] = cherrypy.request.remote_host 37 38 environ["REMOTE_ADDR"] = cherrypy.request.remote_addr … … 51 52 try: 52 53 environ = cherrypy.request.wsgi_environ 54 environ['SCRIPT_NAME'] = cherrypy.request.script_name 55 environ['PATH_INFO'] = cherrypy.request.path_info 53 56 except AttributeError: 54 57 environ = make_environ() 55 environ['SCRIPT_NAME'] = cherrypy.request.script_name56 environ['PATH_INFO'] = cherrypy.request.path_info57 58 58 59 if env: trunk/cherrypy/test/benchmark.py
r1219 r1224 88 88 pass 89 89 90 def run(self, request_line, headers, rfile):90 def run(self, method, path, query_string, protocol, headers, rfile): 91 91 cherrypy.response.status = "204 No Content" 92 92 cherrypy.response.header_list = [("Content-Type", 'text/html'), trunk/cherrypy/test/test_objectmapping.py
r1157 r1224 217 217 self.script_name = "" 218 218 219 # Test absoluteURI's in the Request-Line 220 self.getPage('http://localhost/') 221 self.assertBody('world') 222 219 223 # Test that the "isolated" app doesn't leak url's into the root app. 220 224 # If it did leak, Root.default() would answer with

