Changeset 1253
- Timestamp:
- 08/20/06 01:32:43
- Files:
-
- trunk/cherrypy/_cpwsgi.py (modified) (3 diffs)
- trunk/cherrypy/_cpwsgiserver.py (modified) (11 diffs)
- trunk/cherrypy/config.py (modified) (1 diff)
- trunk/cherrypy/test/helper.py (modified) (1 diff)
- trunk/cherrypy/test/test_conn.py (added)
- trunk/cherrypy/test/webtest.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cpwsgi.py
r1250 r1253 119 119 class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 120 120 121 def __init__(self, socket, addr, server): 122 _cpwsgiserver.HTTPRequest.__init__(self, socket, addr, server) 121 def parse_request(self): 123 122 mhs = int(cherrypy.config.get('server.max_request_header_size', 124 123 500 * 1024)) 125 124 if mhs > 0: 126 125 self.rfile = http.SizeCheckWrapper(self.rfile, mhs) 127 128 def parse_request(self): 126 129 127 try: 130 128 _cpwsgiserver.HTTPRequest.parse_request(self) 131 129 except http.MaxSizeExceeded: 132 msg = "Request Entity Too Large" 133 self.wfile.write("%s 413 %s\r\n" % (self.server.protocol, msg)) 134 self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) 135 self.wfile.write(msg) 136 self.wfile.flush() 137 self.ready = False 138 130 self.abort(413, "Request Entity Too Large") 139 131 cherrypy.log(traceback=True) 140 else: 141 if self.ready: 142 # Request header is parsed 143 script_name = self.environ.get('SCRIPT_NAME', '') 144 path_info = self.environ.get('PATH_INFO', '') 145 path = (script_name + path_info) 146 if path == "*": 147 path = "global" 148 149 if isinstance(self.rfile, http.SizeCheckWrapper): 150 # Unwrap the rfile 151 self.rfile = self.rfile.rfile 152 self.environ["wsgi.input"] = self.rfile 132 133 134 class CPHTTPConnection(_cpwsgiserver.HTTPConnection): 135 136 RequestHandlerClass = CPHTTPRequest 153 137 154 138 … … 163 147 """ 164 148 165 RequestHandlerClass = CPHTTPRequest149 ConnectionClass = CPHTTPConnection 166 150 167 151 def __init__(self): … … 182 166 timeout = conf('server.socket_timeout'), 183 167 ) 184 s.protocol = conf('server.protocol_version', 'HTTP/1. 0')168 s.protocol = conf('server.protocol_version', 'HTTP/1.1') 185 169 trunk/cherrypy/_cpwsgiserver.py
r1237 r1253 35 35 ] 36 36 37 37 38 class HTTPRequest(object): 38 39 39 stderr = sys.stderr 40 bufsize = -1 41 42 def __init__(self, socket, addr, server): 43 self.socket = socket 44 self.addr = addr 45 self.server = server 46 self.environ = {} 40 def __init__(self, connection): 41 self.connection = connection 42 self.rfile = self.connection.rfile 43 self.environ = connection.environ.copy() 44 47 45 self.ready = False 48 46 self.started_response = False 49 47 self.status = "" 50 48 self.outheaders = [] 51 self.outheaderkeys = []52 self.rfile = self.socket.makefile("r", self.bufsize)53 self.wfile = self.socket.makefile("w", self.bufsize)54 49 self.sent_headers = False 50 self.close_connection = False 55 51 56 52 def parse_request(self): 57 self.sent_headers = False 58 self.environ = {} 59 self.environ["wsgi.version"] = (1,0) 60 self.environ["wsgi.url_scheme"] = "http" 61 self.environ["wsgi.input"] = self.rfile 62 self.environ["wsgi.errors"] = self.stderr 63 self.environ["wsgi.multithread"] = True 64 self.environ["wsgi.multiprocess"] = False 65 self.environ["wsgi.run_once"] = False 66 request_line = self.rfile.readline() 53 try: 54 request_line = self.rfile.readline() 55 except socket.timeout: 56 self.abort("408 Request Timeout") 57 67 58 if not request_line: 68 self.ready = False69 59 return 60 61 server = self.connection.server 70 62 71 63 method, path, req_protocol = request_line.strip().split(" ", 2) … … 88 80 path = "%2F".join(atoms) 89 81 90 for mount_point, wsgi_app in se lf.server.mount_points:82 for mount_point, wsgi_app in server.mount_points: 91 83 if path == "*": 92 84 # This means, of course, that the first wsgi_app will … … 123 115 # only return 505 if the _major_ version is different. 124 116 rp = int(req_protocol[5]), int(req_protocol[7]) 125 sp = int(se lf.server.protocol[5]), int(self.server.protocol[7])117 sp = int(server.protocol[5]), int(server.protocol[7]) 126 118 if sp[0] != rp[0]: 127 119 self.abort("505 HTTP Version Not Supported") … … 130 122 131 123 # If the Request-URI was an absoluteURI, use its location atom. 132 self.environ["SERVER_NAME"] = location or self.server.server_name 133 134 if isinstance(self.server.bind_addr, basestring): 135 # AF_UNIX. This isn't really allowed by WSGI, which doesn't 136 # address unix domain sockets. But it's better than nothing. 137 self.environ["SERVER_PORT"] = "" 138 else: 139 self.environ["SERVER_PORT"] = str(self.server.bind_addr[1]) 140 # optional values 141 self.environ["REMOTE_HOST"] = self.addr[0] 142 self.environ["REMOTE_ADDR"] = self.addr[0] 143 self.environ["REMOTE_PORT"] = str(self.addr[1]) 124 if location: 125 self.environ["SERVER_NAME"] = location 144 126 145 127 # then all the http headers 146 128 headers = mimetools.Message(self.rfile) 147 129 self.environ["CONTENT_TYPE"] = headers.getheader("Content-type", "") 130 148 131 cl = headers.getheader("Content-length") 149 132 if method in ("POST", "PUT") and cl is None: … … 163 146 else: 164 147 self.environ[envname] = headers[k] 148 149 if self.environ["SERVER_PROTOCOL"] == "HTTP/1.1": 150 if headers.getheader("Connection", "") == "close": 151 self.close_connection = True 152 self.outheaders.append(("Connection", "close")) 153 else: 154 if headers.getheader("Connection", "") == "Keep-Alive": 155 if self.close_connection == False: 156 self.outheaders.append(("Connection", "Keep-Alive")) 157 else: 158 self.close_connection = True 159 165 160 self.ready = True 161 162 def respond(self): 163 response = self.wsgi_app(self.environ, self.start_response) 164 for line in response: 165 self.write(line) 166 if hasattr(response, "close"): 167 response.close() 168 self.terminate() 166 169 167 170 def abort(self, status, msg=""): 168 171 """Write a simple error message back to the client.""" 169 self.wfile.write("%s %s\r\n" % (self.server.protocol, status)) 170 self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) 172 status = str(status) 173 wfile = self.connection.wfile 174 wfile.write("%s %s\r\n" % (self.connection.server.protocol, status)) 175 wfile.write("Content-Length: %s\r\n" % len(msg)) 176 177 if status[:3] == "413" and self.environ["SERVER_PROTOCOL"] == 'HTTP/1.1': 178 # Request Entity Too Large 179 self.close_connection = True 180 wfile.write("Connection: close\r\n") 181 182 wfile.write("\r\n") 171 183 if msg: 172 self.wfile.write(msg)173 self.wfile.flush()184 wfile.write(msg) 185 wfile.flush() 174 186 self.ready = False 175 187 … … 185 197 self.started_response = True 186 198 self.status = status 187 self.outheaders = headers 188 self.outheaderkeys = [key.lower() for (key,value) in self.outheaders] 199 self.outheaders.extend(headers) 189 200 return self.write 190 201 … … 193 204 self.sent_headers = True 194 205 self.send_headers() 195 self. wfile.write(d)196 self. wfile.flush()206 self.connection.wfile.write(d) 207 self.connection.wfile.flush() 197 208 198 209 def send_headers(self): 199 if "content-length" not in self.outheaderkeys: 200 self.close_at_end = True 201 if "date" not in self.outheaderkeys: 210 hkeys = [key.lower() for (key,value) in self.outheaders] 211 212 if (self.environ["SERVER_PROTOCOL"] == 'HTTP/1.1' 213 and (# Request Entity Too Large. Close conn to avoid garbage. 214 self.status[:3] == "413" 215 # No Content-Length. Close conn to determine transfer-length. 216 or "content-length" not in hkeys)): 217 if "connection" not in hkeys: 218 self.outheaders.append(("Connection", "close")) 219 self.close_connection = True 220 221 if "date" not in hkeys: 202 222 self.outheaders.append(("Date", rfc822.formatdate())) 203 if "server" not in self.outheaderkeys: 204 self.outheaders.append(("Server", self.server.version)) 205 if (self.server.protocol == "HTTP/1.1" 206 and "connection" not in self.outheaderkeys): 207 self.outheaders.append(("Connection", "close")) 208 self.wfile.write(self.server.protocol + " " + self.status + "\r\n") 209 for (k,v) in self.outheaders: 210 self.wfile.write(k + ": " + v + "\r\n") 211 self.wfile.write("\r\n") 212 self.wfile.flush() 223 224 server = self.connection.server 225 wfile = self.connection.wfile 226 227 if "server" not in hkeys: 228 self.outheaders.append(("Server", server.version)) 229 230 wfile.write(server.protocol + " " + self.status + "\r\n") 231 for k, v in self.outheaders: 232 wfile.write(k + ": " + v + "\r\n") 233 wfile.write("\r\n") 234 wfile.flush() 213 235 214 236 def terminate(self): 215 if self.ready and not self.sent_headers and not self.server.interrupt: 237 if (self.ready and not self.sent_headers 238 and not self.connection.server.interrupt): 216 239 self.sent_headers = True 217 240 self.send_headers() 241 242 243 class HTTPConnection(object): 244 245 bufsize = -1 246 RequestHandlerClass = HTTPRequest 247 environ = {"wsgi.version": (1, 0), 248 "wsgi.url_scheme": "http", 249 "wsgi.multithread": True, 250 "wsgi.multiprocess": False, 251 "wsgi.run_once": False, 252 "wsgi.errors": sys.stderr, 253 } 254 255 def __init__(self, socket, addr, server): 256 self.socket = socket 257 self.addr = addr 258 self.server = server 259 260 self.rfile = self.socket.makefile("r", self.bufsize) 261 self.wfile = self.socket.makefile("w", self.bufsize) 262 263 # Copy the class environ into self. 264 self.environ = self.environ.copy() 265 self.environ.update({"wsgi.input": self.rfile, 266 "SERVER_NAME": self.server.server_name, 267 }) 268 269 if isinstance(self.server.bind_addr, basestring): 270 # AF_UNIX. This isn't really allowed by WSGI, which doesn't 271 # address unix domain sockets. But it's better than nothing. 272 self.environ["SERVER_PORT"] = "" 273 else: 274 self.environ["SERVER_PORT"] = str(self.server.bind_addr[1]) 275 # optional values 276 self.environ["REMOTE_HOST"] = self.addr[0] 277 self.environ["REMOTE_ADDR"] = self.addr[0] 278 self.environ["REMOTE_PORT"] = str(self.addr[1]) 279 280 def communicate(self): 281 """Read each request and respond appropriately.""" 282 while True: 283 req = self.RequestHandlerClass(self) 284 # This order of operations should guarantee correct pipelining. 285 req.parse_request() 286 if not req.ready: 287 break 288 req.respond() 289 if req.close_connection: 290 break 291 292 def close(self): 218 293 self.rfile.close() 219 294 self.wfile.close() … … 234 309 self.ready = True 235 310 while True: 236 request= self.server.requests.get()237 if requestis _SHUTDOWNREQUEST:311 conn = self.server.requests.get() 312 if conn is _SHUTDOWNREQUEST: 238 313 return 239 314 240 315 try: 241 316 try: 242 request.parse_request() 243 if request.ready: 244 response = request.wsgi_app(request.environ, 245 request.start_response) 246 for line in response: 247 request.write(line) 248 if hasattr(response, "close"): 249 response.close() 317 conn.communicate() 250 318 except socket.error, e: 251 319 errno = e.args[0] … … 257 325 traceback.print_exc() 258 326 finally: 259 request.terminate()327 conn.close() 260 328 except (KeyboardInterrupt, SystemExit), exc: 261 329 self.server.interrupt = exc … … 278 346 """ 279 347 280 protocol = "HTTP/1. 0"348 protocol = "HTTP/1.1" 281 349 version = "CherryPy/3.0.0alpha" 282 350 ready = False 283 351 _interrupt = None 284 RequestHandlerClass = HTTPRequest352 ConnectionClass = HTTPConnection 285 353 286 354 def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, … … 393 461 if hasattr(s, 'settimeout'): 394 462 s.settimeout(self.timeout) 395 request = self.RequestHandlerClass(s, addr, self)396 self.requests.put( request)463 conn = self.ConnectionClass(s, addr, self) 464 self.requests.put(conn) 397 465 except socket.timeout: 398 466 # The only reason for the timeout in start() is so we can trunk/cherrypy/config.py
r1243 r1253 62 62 'server.socket_queue_size': 5, 63 63 'server.socket_timeout': 10, 64 'server.protocol_version': 'HTTP/1. 0',64 'server.protocol_version': 'HTTP/1.1', 65 65 'server.reverse_dns': False, 66 66 'server.thread_pool': 10, trunk/cherrypy/test/helper.py
r1249 r1253 41 41 pass 42 42 43 def getPage(self, url, headers=None, method="GET", body=None, protocol= "HTTP/1.1"):43 def getPage(self, url, headers=None, method="GET", body=None, protocol=None): 44 44 """Open the url. Return status, headers, body.""" 45 45 if self.script_name: trunk/cherrypy/test/webtest.py
r1186 r1253 140 140 HOST = "127.0.0.1" 141 141 PORT = 8000 142 HTTP_CONN=httplib.HTTPConnection 143 144 def getPage(self, url, headers=None, method="GET", body=None, protocol="HTTP/1.1"): 142 HTTP_CONN = httplib.HTTPConnection 143 PROTOCOL = "HTTP/1.1" 144 145 def getPage(self, url, headers=None, method="GET", body=None, protocol=None): 145 146 """Open the url with debugging support. Return status, headers, body.""" 146 147 ServerError.on = False … … 148 149 self.url = url 149 150 result = openURL(url, headers, method, body, self.HOST, self.PORT, 150 self.HTTP_CONN, protocol )151 self.HTTP_CONN, protocol or self.PROTOCOL) 151 152 self.status, self.headers, self.body = result 152 153 … … 367 368 while trial < 10: 368 369 try: 369 conn = http_conn(host, port) 370 # Allow http_conn to be a class or an instance 371 if hasattr(http_conn, "host"): 372 conn = http_conn 373 else: 374 conn = http_conn(host, port) 375 370 376 conn._http_vsn_str = protocol 371 377 conn._http_vsn = int("".join([x for x in protocol if x.isdigit()])) … … 407 413 outbody = response.read() 408 414 409 conn.close() 415 if not hasattr(http_conn, "host"): 416 # We made our own conn instance. Close it. 417 conn.close() 418 410 419 return status, outheaders, outbody 411 420 except socket.error:

