Changeset 1238
- Timestamp:
- 08/12/06 01:42:49
- Files:
-
- branches/cherrypy-2.x/cherrypy/__init__.py (modified) (1 diff)
- branches/cherrypy-2.x/cherrypy/_cpwsgiserver.py (modified) (13 diffs)
- branches/cherrypy-2.x/cherrypy/test/test_objectmapping.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/cherrypy-2.x/cherrypy/__init__.py
r1065 r1238 1 1 """Global module that all modules developing with CherryPy should import.""" 2 2 3 __version__ = '2.2. 1'3 __version__ = '2.2.2rc1' 4 4 5 5 import datetime branches/cherrypy-2.x/cherrypy/_cpwsgiserver.py
r1113 r1238 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 sys8 11 import time 9 12 import traceback 10 11 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 12 monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 13 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 13 from urllib import unquote 14 from urlparse import urlparse 14 15 15 16 import errno … … 25 26 socket_errors_to_ignore = dict.fromkeys(socket_errors_to_ignore).keys() 26 27 28 # These are lowercase because mimetools.Message uses lowercase keys. 29 comma_separated_headers = [ 30 'accept', 'accept-charset', 'accept-encoding', 'accept-language', 31 'accept-ranges', 'allow', 'cache-control', 'connection', 'content-encoding', 32 'content-language', 'expect', 'if-match', 'if-none-match', 'pragma', 33 'proxy-authenticate', 'te', 'trailer', 'transfer-encoding', 'upgrade', 34 'vary', 'via', 'warning', 'www-authenticate', 35 ] 27 36 28 37 class HTTPRequest(object): … … 59 68 self.ready = False 60 69 return 61 method,path,version = request_line.strip().split(" ", 2) 62 if "?" in path: 63 path, qs = path.split("?", 1) 64 else: 65 qs = "" 70 71 method, path, req_protocol = request_line.strip().split(" ", 2) 66 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 scheme: 77 self.environ["wsgi.url_scheme"] = scheme 78 if params: 79 path = path + ";" + params 80 81 # Unquote the path+params (e.g. "/this%20path" -> "this path"). 82 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 83 # 84 # But note that "...a URI must be separated into its components 85 # before the escaped characters within those components can be 86 # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2 87 atoms = [unquote(x) for x in quoted_slash.split(path)] 88 path = "%2F".join(atoms) 67 89 68 90 for mount_point, wsgi_app in self.server.mount_points: … … 84 106 return 85 107 108 # Note that, like wsgiref and most other WSGI servers, 109 # we unquote the path but not the query string. 86 110 self.environ["QUERY_STRING"] = qs 87 self.environ["SERVER_PROTOCOL"] = version 88 self.environ["SERVER_NAME"] = self.server.server_name 111 self.environ["SERVER_PROTOCOL"] = req_protocol 112 # If the Request-URI was an absoluteURI, use its location atom. 113 self.environ["SERVER_NAME"] = location or self.server.server_name 114 89 115 if isinstance(self.server.bind_addr, basestring): 90 116 # AF_UNIX. This isn't really allowed by WSGI, which doesn't … … 97 123 self.environ["REMOTE_ADDR"] = self.addr[0] 98 124 self.environ["REMOTE_PORT"] = str(self.addr[1]) 125 99 126 # then all the http headers 100 127 headers = mimetools.Message(self.rfile) … … 111 138 self.environ["CONTENT_LENGTH"] = cl or "" 112 139 113 for (k, v) in headers.items(): 114 envname = "HTTP_" + k.upper().replace("-","_") 115 self.environ[envname] = v 140 for k in headers: 141 envname = "HTTP_" + k.upper().replace("-", "_") 142 if k in comma_separated_headers: 143 self.environ[envname] = ", ".join(headers.getheaders(k)) 144 else: 145 self.environ[envname] = headers[k] 116 146 self.ready = True 117 147 … … 152 182 self.close_at_end = True 153 183 if "date" not in self.outheaderkeys: 154 # HTTP 1.1 mandates date output in RFC 1123 format. 155 year, month, day, hh, mm, ss, wd, y, z = time.gmtime() 156 dt = ("%s, %02d %3s %4d %02d:%02d:%02d GMT" % 157 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)) 158 self.outheaders.append(("Date", dt)) 184 self.outheaders.append(("Date", rfc822.formatdate())) 159 185 if "server" not in self.outheaderkeys: 160 186 self.outheaders.append(("Server", self.server.version)) … … 219 245 220 246 class CherryPyWSGIServer(object): 221 222 version = "CherryPy/2.2.1" 247 """An HTTP server for WSGI. 248 249 bind_addr: a (host, port) tuple if TCP sockets are desired; 250 for UNIX sockets, supply the filename as a string. 251 wsgi_app: the WSGI 'application callable'; multiple WSGI applications 252 may be passed as (script_name, callable) pairs. 253 numthreads: the number of worker threads to create (default 10). 254 server_name: the string to set for WSGI's SERVER_NAME environ entry. 255 Defaults to socket.gethostname(). 256 max: the maximum number of queued requests (defaults to -1 = no limit). 257 request_queue_size: the 'backlog' argument to socket.listen(); 258 specifies the maximum number of queued connections (default 5). 259 timeout: the timeout in seconds for accepted connections (default 10). 260 """ 261 262 version = "CherryPy/2.2.2rc1" 263 protocol = "HTTP/1.0" 223 264 ready = False 224 265 interrupt = None … … 227 268 def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 228 269 max=-1, request_queue_size=5, timeout=10): 229 """Be careful w/ max"""230 270 self.requests = Queue.Queue(max) 231 271 … … 258 298 # If you're using this server with another framework, you should 259 299 # trap those exceptions in whatever code block calls start(). 300 301 def bind(family, type, proto=0): 302 """Create (or recreate) the actual socket object.""" 303 self.socket = socket.socket(family, type, proto) 304 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 305 self.socket.bind(self.bind_addr) 260 306 261 307 # Select the appropriate socket … … 328 374 try: 329 375 s, addr = self.socket.accept() 376 if not self.ready: 377 return 330 378 if hasattr(s, 'settimeout'): 331 379 s.settimeout(self.timeout) … … 337 385 # accept() by default 338 386 return 387 except socket.error, x: 388 if x.args[1] == "Bad file descriptor": 389 # Our socket was closed 390 return 391 raise 339 392 340 393 def stop(self): … … 352 405 # Don't join currentThread (when stop is called inside a request). 353 406 current = threading.currentThread() 354 for worker in self._workerThreads: 407 while self._workerThreads: 408 worker = self._workerThreads.pop() 355 409 if worker is not current and worker.isAlive: 356 worker.join() 357 358 self._workerThreads = [] 410 try: 411 worker.join() 412 except AssertionError: 413 pass 414 branches/cherrypy-2.x/cherrypy/test/test_objectmapping.py
r1017 r1238 205 205 206 206 self.mount_point = "" 207 208 # Test absoluteURI's in the Request-Line 209 self.getPage('http://localhost/') 210 self.assertBody('world') 207 211 208 212 # Test that the "isolated" app doesn't leak url's into the root app.

