Changeset 693
- Timestamp:
- 09/29/05 18:37:37
- Files:
-
- trunk/cherrypy/_cphttpserver.py (modified) (10 diffs)
- trunk/cherrypy/_cphttptools.py (modified) (2 diffs)
- trunk/cherrypy/_cpwsgi.py (modified) (2 diffs)
- trunk/cherrypy/_cpwsgiserver.py (modified) (7 diffs)
- trunk/cherrypy/server.py (modified) (3 diffs)
- trunk/cherrypy/test/helper.py (modified) (2 diffs)
- trunk/cherrypy/test/test_core.py (modified) (2 diffs)
- trunk/cherrypy/test/test_states.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cphttpserver.py
r691 r693 31 31 import cherrypy 32 32 from cherrypy import _cputil, _cphttptools 33 33 34 34 35 class CherryHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): … … 136 137 137 138 138 class CherryHTTPServer(BaseHTTPServer.HTTPServer): 139 class CherryHTTPServer(SocketServer.TCPServer): 140 # Subclass TCPServer (instead of BaseHTTPServer.HTTPServer), because 141 # getfqdn call was timing out on localhost when calling gethostbyaddr. 139 142 140 143 ready = False 144 interrupt = None 145 146 allow_reuse_address = True 141 147 142 148 def __init__(self): … … 167 173 self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 168 174 169 BaseHTTPServer.HTTPServer.__init__(self, server_address, CherryHTTPRequestHandler)175 SocketServer.TCPServer.__init__(self, server_address, CherryHTTPRequestHandler) 170 176 171 177 def server_activate(self): 172 178 """Override server_activate to set timeout on our listener socket""" 173 179 self.socket.settimeout(1) 174 BaseHTTPServer.HTTPServer.server_activate(self) 175 176 def server_bind(self): 177 # Removed getfqdn call because it was timing out 178 # on localhost when calling gethostbyaddr 179 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 180 self.socket.bind(self.server_address) 180 SocketServer.TCPServer.server_activate(self) 181 181 182 182 def get_request(self): … … 192 192 193 193 def handle_request(self): 194 """Override handle_request to trap timeout exception.""" 194 """Handle one request, possibly blocking.""" 195 # Overridden to trap socket.timeout and KeyboardInterrupt/SystemExit. 195 196 try: 196 BaseHTTPServer.HTTPServer.handle_request(self) 197 request, client_address = self.get_request() 198 except socket.error: 199 return 197 200 except socket.timeout: 198 201 # The only reason for the timeout is so we can notice keyboard 199 202 # interrupts on Win32, which don't interrupt accept() by default 200 203 return 1 204 205 if self.verify_request(request, client_address): 206 try: 207 self.process_request(request, client_address) 208 except (KeyboardInterrupt, SystemExit): 209 self.close_request(request) 210 raise 211 except: 212 self.handle_error(request, client_address) 213 self.close_request(request) 214 215 def handle_error(self, request, client_address): 216 errorBody = _cputil.formatExc() 217 cherrypy.log(errorBody) 201 218 202 219 def serve_forever(self): 203 220 """Override serve_forever to handle shutdown.""" 204 self.__running = 1205 221 self.ready = True 206 while self. __running:222 while self.ready: 207 223 self.handle_request() 224 if self.interrupt: 225 i = self.interrupt 226 self.interrupt = None 227 raise i 208 228 start = serve_forever 209 229 210 230 def shutdown(self): 211 self. __running = 0231 self.ready = False 212 232 # Close the socket 213 233 self.server_close() … … 219 239 class ServerThread(threading.Thread): 220 240 221 def __init__(self, RequestHandlerClass, requestQueue): 241 def __init__(self, RequestHandlerClass, requestQueue, server): 242 self.server = server 222 243 self.ready = False 223 244 threading.Thread.__init__(self) … … 226 247 227 248 def run(self): 228 self.ready = True 229 while 1: 230 request, client_address = self._requestQueue.get() 231 if (request, client_address) == _SHUTDOWNREQUEST: 232 return 233 if self.verify_request(request, client_address): 234 try: 235 self.process_request(request, client_address) 236 except: 237 self.handle_error(request, client_address) 249 try: 250 self.ready = True 251 while 1: 252 request, client_address = self._requestQueue.get() 253 if (request, client_address) == _SHUTDOWNREQUEST: 254 return 255 if self.verify_request(request, client_address): 256 try: 257 self.process_request(request, client_address) 258 except (KeyboardInterrupt, SystemExit): 259 self.close_request(request) 260 raise 261 except: 262 self.handle_error(request, client_address) 263 self.close_request(request) 264 else: 238 265 self.close_request(request) 239 else:240 self.close_request(request)266 except (KeyboardInterrupt, SystemExit), exc: 267 self.server.interrupt = exc 241 268 242 269 def verify_request(self, request, client_address): … … 271 298 allow_reuse_address = 1 272 299 ready = False 300 interrupt = None 273 301 274 302 def __init__(self): … … 297 325 298 326 def createThread(self): 299 return self._ThreadClass(self._RequestHandlerClass, self._requestQueue )327 return self._ThreadClass(self._RequestHandlerClass, self._requestQueue, self) 300 328 301 329 def server_activate(self): … … 304 332 SocketServer.TCPServer.server_activate(self) 305 333 306 def server_bind(self):307 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)308 self.socket.bind(self.server_address)309 310 334 def shutdown(self): 311 335 """Gracefully shutdown a server that is serve_forever()ing.""" 312 self. __running = 0336 self.ready = False 313 337 # Close the socket so restarts work. 314 338 self.server_close() … … 333 357 worker.start() 334 358 335 self.__running = 1336 337 359 for worker in self._workerThreads: 338 360 while not worker.ready: 339 361 time.sleep(.1) 362 340 363 self.ready = True 341 342 while self.__running: 364 while self.ready: 365 if self.interrupt: 366 i = self.interrupt 367 self.interrupt = None 368 raise i 343 369 if not self.handle_request(): 344 370 break trunk/cherrypy/_cphttptools.py
r682 r693 285 285 finally: 286 286 applyFilters('onEndResource') 287 except (KeyboardInterrupt, SystemExit): 288 raise 287 289 except: 288 290 handleError(sys.exc_info()) … … 436 438 finalize() 437 439 return 440 except (KeyboardInterrupt, SystemExit): 441 raise 438 442 except: 439 443 # Fall through to the second error handler 440 444 pass 445 except (KeyboardInterrupt, SystemExit): 446 raise 441 447 except: 442 448 # Fall through to the second error handler trunk/cherrypy/_cpwsgi.py
r665 r693 110 110 environ['wsgi.url_scheme'], 111 111 ) 112 except (KeyboardInterrupt, SystemExit): 113 raise 112 114 except: 113 115 tb = _cputil.formatExc() … … 131 133 chunk = str(chunk) 132 134 yield chunk 135 except (KeyboardInterrupt, SystemExit): 136 raise 133 137 except: 134 138 tb = _cputil.formatExc() trunk/cherrypy/_cpwsgiserver.py
r691 r693 103 103 self.addr = addr 104 104 self.server = server 105 self.environ = None105 self.environ = {} 106 106 self.ready = False 107 107 self.started_response = False 108 self.status = None109 self.outheaders = None110 self.outheaderkeys = None108 self.status = "" 109 self.outheaders = [] 110 self.outheaderkeys = [] 111 111 self.rfile = self.socket.makefile("r", self.server.bufsize) 112 112 if self.server.config: … … 212 212 213 213 def terminate(self): 214 if self.ready and not self.sent_headers :214 if self.ready and not self.sent_headers and not self.server.interrupt: 215 215 self.sent_headers = True 216 216 self.send_headers() … … 230 230 231 231 def run(self): 232 self.ready = True233 while self.server._running:234 request = self.server.requests.get()235 if request is _SHUTDOWNREQUEST:236 return237 238 try:232 try: 233 self.ready = True 234 while True: 235 request = self.server.requests.get() 236 if request is _SHUTDOWNREQUEST: 237 return 238 239 239 try: 240 request.parse_request() 241 if request.ready: 242 response = self.server.wsgi_app(request.environ, 243 request.start_response) 244 for line in response: 245 request.write(line) 246 except socket.error, e: 247 errno = e.args[0] 248 if errno in (32, 104, 10053, 10054): 249 # Client probably closed the connection before the 250 # response was sent. 251 pass 252 else: 253 raise 254 except MaxSizeExceeded: 255 str = "Request Entity Too Large" 256 proto = request.environ.get("SERVER_PROTOCOL", "HTTP/1.0") 257 request.wfile.write("%s 413 %s\r\n" % (proto, str)) 258 request.wfile.write("Content-Length: %s\r\n\r\n" % len(str)) 259 request.wfile.write(str) 260 request.wfile.flush() 261 except: 262 traceback.print_exc() 263 finally: 264 request.terminate() 240 try: 241 request.parse_request() 242 if request.ready: 243 response = self.server.wsgi_app(request.environ, 244 request.start_response) 245 for line in response: 246 request.write(line) 247 except socket.error, e: 248 errno = e.args[0] 249 if errno in (32, 104, 10053, 10054): 250 # Client probably closed the connection before the 251 # response was sent. 252 pass 253 else: 254 raise 255 except MaxSizeExceeded: 256 str = "Request Entity Too Large" 257 proto = request.environ.get("SERVER_PROTOCOL", "HTTP/1.0") 258 request.wfile.write("%s 413 %s\r\n" % (proto, str)) 259 request.wfile.write("Content-Length: %s\r\n\r\n" % len(str)) 260 request.wfile.write(str) 261 request.wfile.flush() 262 except (KeyboardInterrupt, SystemExit), exc: 263 self.server.interrupt = exc 264 except: 265 traceback.print_exc() 266 finally: 267 request.terminate() 268 except (KeyboardInterrupt, SystemExit), exc: 269 self.server.interrupt = exc 265 270 266 271 … … 269 274 version = "CherryPy/2.1.0-rc1" 270 275 ready = False 276 interrupt = None 271 277 272 278 def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, … … 304 310 self.socket.listen(5) 305 311 306 self._running = True307 308 312 # Create worker threads 309 313 for i in xrange(self.numthreads): … … 314 318 while not worker.ready: 315 319 time.sleep(.1) 320 316 321 self.ready = True 317 318 while self._running: 322 while self.ready: 319 323 self.tick() 320 321 def stop(self): 322 """Gracefully shutdown a server that is serving forever.""" 323 self._running = False 324 self.socket.close() 325 326 # Must shut down threads here so the code that calls 327 # this method can know when all threads are stopped. 328 for worker in self._workerThreads: 329 self.requests.put(_SHUTDOWNREQUEST) 330 331 # Don't join currentThread (when stop is called inside a request). 332 current = threading.currentThread() 333 for worker in self._workerThreads: 334 if worker is not current: 335 worker.join() 336 337 self._workerThreads = [] 324 if self.interrupt: 325 i = self.interrupt 326 self.interrupt = None 327 raise i 338 328 339 329 def tick(self): … … 351 341 # accept() by default 352 342 return 343 344 def stop(self): 345 """Gracefully shutdown a server that is serving forever.""" 346 self.ready = False 347 self.socket.close() 348 349 # Must shut down threads here so the code that calls 350 # this method can know when all threads are stopped. 351 for worker in self._workerThreads: 352 self.requests.put(_SHUTDOWNREQUEST) 353 354 # Don't join currentThread (when stop is called inside a request). 355 current = threading.currentThread() 356 for worker in self._workerThreads: 357 if worker is not current: 358 worker.join() 359 360 self._workerThreads = [] trunk/cherrypy/server.py
r691 r693 158 158 # This should block until the http server stops. 159 159 cherrypy._httpserver.start() 160 except (KeyboardInterrupt, SystemExit):160 except KeyboardInterrupt: 161 161 cherrypy.log("<Ctrl-C> hit: shutting down server", "HTTP") 162 stop() 163 except SystemExit: 164 cherrypy.log("SystemExit raised: shutting down server", "HTTP") 162 165 stop() 163 166 … … 271 274 272 275 def wait_for_free_port(host, port): 276 """Wait for the specified port to become free (drop requests).""" 273 277 for trial in xrange(50): 274 278 try: … … 284 288 285 289 def wait_for_occupied_port(host, port): 290 """Wait for the specified port to become active (receive requests).""" 286 291 for trial in xrange(50): 287 292 try: trunk/cherrypy/test/helper.py
r691 r693 156 156 from cherrypy._cputil import getErrorPage 157 157 esc = re.escape 158 # This will never contain a traceback: 158 159 page = esc(getErrorPage(status, message=message)) 159 160 … … 164 165 m = re.match(page, self.body, re.DOTALL) 165 166 if not m: 166 self._handlewebError('Error page does not match ')167 self._handlewebError('Error page does not match\n' + page) 167 168 return 168 169 trunk/cherrypy/test/test_core.py
r692 r693 224 224 225 225 if m == "LINK": 226 cherrypy.response.status = 405226 raise cherrypy.HTTPError(405) 227 227 else: 228 cherrypy.response.status = 501228 raise cherrypy.HTTPError(501) 229 229 230 230 def parameterized(self, data): … … 606 606 # Test that all defined HTTP methods work. 607 607 for m in defined_http_methods: 608 h = []609 608 self.getPage("/method/", method=m) 610 609 trunk/cherrypy/test/test_states.py
r691 r693 52 52 import helper 53 53 54 54 55 class ServerStateTests(helper.CPWebCase): 55 56 … … 78 79 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 79 80 80 def test_1_KeyboardInterrupt s(self):81 def test_1_KeyboardInterrupt(self): 81 82 if self.serverClass: 82 83 # Raise a keyboard interrupt in the HTTP server's main thread. 84 helper.startServer(self.serverClass) 85 cherrypy._httpserver.interrupt = KeyboardInterrupt 86 # Give the server time to shut down. 87 while cherrypy._appserver_state != 0: 88 time.sleep(.1) 89 self.assertEqual(cherrypy._httpserver, None) 90 self.assertEqual(cherrypy._appserver_state, 0) 91 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 83 92 84 def raiser(x=None): 85 raise KeyboardInterrupt 86 93 # Raise a keyboard interrupt in a page handler; on multithreaded 94 # servers, this should occur in one of the worker threads. 95 # This should raise a BadStatusLine error, since the worker 96 # thread will just die without writing a response. 97 from httplib import BadStatusLine 87 98 helper.startServer(self.serverClass) 88 89 name = cherrypy._httpserver.__class__.__name__ 90 if name == "WSGIServer": 91 cherrypy._httpserver.tick = raiser 92 elif name in ("CherryHTTPServer", "PooledThreadServer"): 93 cherrypy._httpserver.handle_request = raiser 94 else: 95 raise ValueError("Unknown HTTP server: %s" % name) 96 99 self.assertRaises(BadStatusLine, self.getPage, "/ctrlc") 100 # Give the server time to shut down. 101 while cherrypy._appserver_state != 0: 102 print ".", 103 time.sleep(.1) 104 self.assertEqual(cherrypy._httpserver, None) 105 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 106 107 def test_2_Restart(self): 108 # Test server start 109 helper.startServer(self.serverClass) 110 self.getPage("/") 111 self.assertBody("Hello World") 112 113 # Test server restart 114 cherrypy.server.restart() 115 self.assertEqual(cherrypy._appserver_state, 1) 116 self.getPage("/") 117 self.assertBody("Hello World") 118 119 # Now that we've restarted, test a KeyboardInterrupt (ticket 321). 120 if self.serverClass: 121 cherrypy._httpserver.interrupt = KeyboardInterrupt 97 122 # Give the server time to shut down. 98 123 while cherrypy._appserver_state != 0: … … 102 127 # Once the server has stopped, we should get a NotReady error again. 103 128 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 104 105 def test_2_Restart(self):106 # Test server start107 import cherrypy108 109 helper.startServer(self.serverClass)110 self.getPage("/")111 self.assertBody("Hello World")112 113 # Test server restart114 cherrypy.server.restart()115 116 self.assertEqual(cherrypy._appserver_state, 1)117 self.getPage("/")118 self.assertBody("Hello World")119 129 120 130

