Changeset 843
- Timestamp:
- 11/27/05 21:08:38
- Files:
-
- trunk/cherrypy/_cphttpserver.py (modified) (9 diffs)
- trunk/cherrypy/_cphttptools.py (modified) (2 diffs)
- trunk/cherrypy/_cpwsgi.py (modified) (3 diffs)
- trunk/cherrypy/_cpwsgiserver.py (modified) (9 diffs)
- trunk/cherrypy/lib/httptools.py (modified) (1 diff)
- trunk/cherrypy/test/test_core.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cphttpserver.py
r838 r843 1 1 """A native HTTP server for CherryPy.""" 2 2 3 import threading, os, socket, time 4 import SocketServer, BaseHTTPServer, Queue 3 from BaseHTTPServer import BaseHTTPRequestHandler 4 import os 5 import Queue 6 import socket 7 import SocketServer 8 import threading 9 import time 10 5 11 import cherrypy 6 12 from cherrypy import _cputil 7 8 9 class CherryHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 10 11 """CherryPy HTTP request handler with the following commands: 12 13 o GET 14 o HEAD 15 o POST 16 o HOTRELOAD 17 18 """ 13 from cherrypy.lib import httptools 14 15 16 class CherryHTTPRequestHandler(BaseHTTPRequestHandler): 17 """CherryPy HTTP request handler""" 19 18 20 19 def address_string(self): 21 """ Try to do a reverse DNS based on [server]reverseDNS in the config file """20 """ Try to do a reverse DNS based on server.reverseDNS in the config file """ 22 21 if cherrypy.config.get('server.reverseDNS'): 23 return BaseHTTP Server.BaseHTTPRequestHandler.address_string(self)22 return BaseHTTPRequestHandler.address_string(self) 24 23 else: 25 24 return self.client_address[0] 26 25 27 26 def _headerlist(self): 28 list = []27 hlist = [] 29 28 hit = 0 30 29 for line in self.headers.headers: … … 32 31 if line[0] in ' \t': 33 32 # Continuation line. Add to previous entry. 34 if list:35 list[-1][1] += " " + line.lstrip()33 if hlist: 34 hlist[-1][1] += " " + line.lstrip() 36 35 else: 37 36 # New header. Add a new entry. We don't catch 38 37 # ValueError here because we trust rfc822.py. 39 38 name, value = line.split(":", 1) 40 list.append((name.strip(), value.strip())) 41 return list 39 hlist.append((name.strip(), value.strip())) 40 return hlist 41 42 def parse_request(self): 43 # Extended to provide header and body length limits. 44 mhs = int(cherrypy.config.get('server.maxRequestHeaderSize', 45 500 * 1024)) 46 self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs) 47 try: 48 presult = BaseHTTPRequestHandler.parse_request(self) 49 except httptools.MaxSizeExceeded: 50 self.send_error(413, "Request Entity Too Large") 51 tb = _cputil.formatExc() 52 cherrypy.log(tb) 53 return False 54 else: 55 if presult: 56 # Request header is parsed 57 # We prepare the SizeCheckWrapper for the request body 58 self.rfile.bytes_read = 0 59 path = self.path 60 if path == "*": 61 path = "global" 62 mbs = int(cherrypy.config.get('server.maxRequestBodySize', 63 100 * 1024 * 1024, path=path)) 64 self.rfile.maxlen = mbs 65 return presult 42 66 43 67 def handle_one_request(self): … … 107 131 108 132 109 class CherryHTTPServer(SocketServer. TCPServer):110 # Subclass TCPServer (instead of BaseHTTPServer.HTTPServer), because133 class CherryHTTPServer(SocketServer.BaseServer): 134 # Subclass BaseServer (instead of BaseHTTPServer.HTTPServer), because 111 135 # getfqdn call was timing out on localhost when calling gethostbyaddr. 112 136 113 137 ready = False 114 138 interrupt = None 115 139 RequestHandlerClass = CherryHTTPRequestHandler 116 140 allow_reuse_address = True 117 141 118 142 def __init__(self): 143 # SocketServer __init__'s all say "do not override", 144 # but we have to in order to implement SSL and IPv6 support! 145 119 146 # Set protocol_version 120 proto = cherrypy.config.get('server.protocolVersion') or "HTTP/1.0" 121 CherryHTTPRequestHandler.protocol_version = proto 147 httpproto = cherrypy.config.get('server.protocolVersion') or "HTTP/1.0" 148 self.RequestHandlerClass.protocol_version = httpproto 149 150 self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 122 151 123 152 # Select the appropriate server based on config options … … 135 164 except: pass 136 165 137 server_address = sockFile 166 self.server_address = sockFile 167 self.socket = socket.socket(self.address_family, self.socket_type) 168 self.server_bind() 138 169 else: 139 # AF_INET socket 140 server_address = (cherrypy.config.get('server.socketHost'), 141 cherrypy.config.get('server.socketPort')) 142 143 self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 144 145 SocketServer.TCPServer.__init__(self, server_address, CherryHTTPRequestHandler) 170 # AF_INET or AF_INET6 socket 171 host = cherrypy.config.get('server.socketHost') 172 port = cherrypy.config.get('server.socketPort') 173 self.server_address = (host, port) 174 175 # Get the correct address family for our host (allows IPv6 addresses) 176 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 177 socket.SOCK_STREAM): 178 af, socktype, proto, canonname, sa = res 179 self.address_family = af 180 self.socket_type = socktype 181 try: 182 self.socket = socket.socket(af, socktype, proto) 183 self.server_bind() 184 except socket.error, msg: 185 if self.socket: 186 self.socket.close() 187 self.socket = None 188 continue 189 break 190 191 if not self.socket: 192 raise socket.error, msg 193 194 self.server_activate() 146 195 147 196 def server_activate(self): 148 197 """Override server_activate to set timeout on our listener socket""" 149 198 self.socket.settimeout(1) 150 SocketServer.TCPServer.server_activate(self) 199 self.socket.listen(self.request_queue_size) 200 201 def server_bind(self): 202 """Called by constructor to bind the socket.""" 203 if self.allow_reuse_address: 204 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 205 self.socket.bind(self.server_address) 206 207 def close_request(self, request): 208 """Called to clean up an individual request.""" 209 request.close() 151 210 152 211 def get_request(self): … … 156 215 # (where wfile and rfile are set in SocketServer.py) we explicitly 157 216 # set the request socket to blocking 158 159 217 request, client_address = self.socket.accept() 160 request.setblocking(1) 218 if hasattr(request, 'setblocking'): 219 request.setblocking(1) 161 220 return request, client_address 162 221 … … 166 225 try: 167 226 request, client_address = self.get_request() 168 except socket.error: 169 return 170 except socket.timeout: 227 except (socket.error, socket.timeout): 171 228 # The only reason for the timeout is so we can notice keyboard 172 229 # interrupts on Win32, which don't interrupt accept() by default 173 return 1 174 175 if self.verify_request(request, client_address): 176 try: 177 self.process_request(request, client_address) 178 except (KeyboardInterrupt, SystemExit): 179 self.close_request(request) 180 raise 181 except: 182 self.handle_error(request, client_address) 183 self.close_request(request) 230 return 231 232 try: 233 self.process_request(request, client_address) 234 except (KeyboardInterrupt, SystemExit): 235 self.close_request(request) 236 raise 237 except: 238 self.handle_error(request, client_address) 239 self.close_request(request) 184 240 185 241 def handle_error(self, request, client_address): 186 errorBody = _cputil.formatExc() 187 cherrypy.log(errorBody) 242 cherrypy.log(_cputil.formatExc()) 188 243 189 244 def serve_forever(self): … … 191 246 self.ready = True 192 247 while self.ready: 193 self.handle_request()194 248 if self.interrupt: 195 249 raise self.interrupt 250 self.handle_request() 251 self.server_close() 196 252 start = serve_forever 197 253 198 def s hutdown(self):254 def server_close(self): 199 255 self.ready = False 200 # Close the socket 201 self.server_close() 202 stop = shutdown 203 204 205 _SHUTDOWNREQUEST = (0,0) 206 207 class ServerThread(threading.Thread): 208 209 def __init__(self, RequestHandlerClass, requestQueue, server): 210 self.server = server 211 self.ready = False 212 threading.Thread.__init__(self) 213 self._RequestHandlerClass = RequestHandlerClass 214 self._requestQueue = requestQueue 215 216 def run(self): 217 try: 218 self.ready = True 219 while 1: 220 request, client_address = self._requestQueue.get() 221 if (request, client_address) == _SHUTDOWNREQUEST: 222 return 223 if self.verify_request(request, client_address): 224 try: 225 self.process_request(request, client_address) 226 except (KeyboardInterrupt, SystemExit): 227 self.close_request(request) 228 raise 229 except: 230 self.handle_error(request, client_address) 231 self.close_request(request) 232 else: 233 self.close_request(request) 234 except (KeyboardInterrupt, SystemExit), exc: 235 self.server.interrupt = exc 236 237 def verify_request(self, request, client_address): 238 """ Verify the request. May be overridden. 239 Return 1 if we should proceed with this request. """ 240 return 1 241 242 def process_request(self, request, client_address): 243 self._RequestHandlerClass(request, client_address, self) 244 self.close_request(request) 245 246 def close_request(self, request): 247 """ Called to clean up an individual request. """ 248 request.close() 249 250 def handle_error(self, request, client_address): 251 """Handle an error gracefully. May be overridden.""" 252 errorBody = _cputil.formatExc() 253 cherrypy.log(errorBody) 254 255 256 class PooledThreadServer(SocketServer.TCPServer): 256 self.socket.close() 257 stop = shutdown = server_close 258 259 260 class PooledThreadServer(CherryHTTPServer): 257 261 """A TCP Server using a pool of worker threads. This is superior to the 258 262 alternatives provided by the Python standard library, which only offer … … 264 268 requests (i.e. you don't have to bother with Deferreds).""" 265 269 266 allow_reuse_address = 1267 ready = False268 interrupt = None269 270 270 def __init__(self): 271 # Set protocol_version 272 proto = cherrypy.config.get('server.protocolVersion') or "HTTP/1.0" 273 CherryHTTPRequestHandler.protocol_version = proto 274 275 # Select the appropriate server based on config options 276 threadPool = cherrypy.config.get('server.threadPool') 277 server_address = (cherrypy.config.get('server.socketHost'), 278 cherrypy.config.get('server.socketPort')) 279 self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 280 281 # I know it says "do not override", 282 # but I have to in order to implement SSL support ! 283 SocketServer.BaseServer.__init__(self, server_address, CherryHTTPRequestHandler) 284 self.socket = socket.socket(self.address_family, self.socket_type) 285 self.server_bind() 286 self.server_activate() 287 288 self._numThreads = threadPool 289 self._RequestHandlerClass = CherryHTTPRequestHandler 290 self._ThreadClass = ServerThread 291 self._requestQueue = Queue.Queue() 292 self._workerThreads = [] 271 self.numThreads = cherrypy.config.get('server.threadPool') 272 self.ThreadClass = ServerThread 273 self.requestQueue = Queue.Queue() 274 self.workerThreads = [] 275 CherryHTTPServer.__init__(self) 276 277 def process_request(self, request, client_address): 278 """Call finish_request.""" 279 self.finish_request(request, client_address) 280 # Let the ServerThread close the request when it's finished. 281 ## NO! self.close_request(request) 282 283 def finish_request(self, request, client_address): 284 """Finish one request by passing it to the Queue.""" 285 self.requestQueue.put((request, client_address)) 293 286 294 287 def createThread(self): 295 return self._ThreadClass(self._RequestHandlerClass, self._requestQueue, self) 296 297 def server_activate(self): 298 """Override server_activate to set timeout on our listener socket""" 299 self.socket.settimeout(1) 300 SocketServer.TCPServer.server_activate(self) 301 302 def shutdown(self): 288 return self.ThreadClass(self.RequestHandlerClass, self.requestQueue, self) 289 290 def serve_forever(self): 291 """Handle one request at a time until doomsday (or shutdown is called).""" 292 if self.workerThreads == []: 293 for i in xrange(self.numThreads): 294 self.workerThreads.append(self.createThread()) 295 for worker in self.workerThreads: 296 worker.start() 297 298 for worker in self.workerThreads: 299 while not worker.ready: 300 time.sleep(.1) 301 302 CherryHTTPServer.serve_forever(self) 303 start = serve_forever 304 305 def server_close(self): 303 306 """Gracefully shutdown a server that is serve_forever()ing.""" 304 self.ready = False 305 # Close the socket so restarts work. 306 self.server_close() 307 CherryHTTPServer.server_close(self) 307 308 308 309 # Must shut down threads here so the code that calls 309 310 # this method can know when all threads are stopped. 310 for worker in self. _workerThreads:311 self. _requestQueue.put(_SHUTDOWNREQUEST)311 for worker in self.workerThreads: 312 self.requestQueue.put(_SHUTDOWNREQUEST) 312 313 current = threading.currentThread() 313 for worker in self. _workerThreads:314 for worker in self.workerThreads: 314 315 if worker is not current and worker.isAlive: 315 316 worker.join() 316 self._workerThreads = [] 317 stop = shutdown 318 319 def serve_forever(self): 320 """Handle one request at a time until doomsday (or shutdown is called).""" 321 if self._workerThreads == []: 322 for i in xrange(self._numThreads): 323 self._workerThreads.append(self.createThread()) 324 for worker in self._workerThreads: 325 worker.start() 326 327 for worker in self._workerThreads: 328 while not worker.ready: 329 time.sleep(.1) 330 331 self.ready = True 332 while self.ready: 333 if self.interrupt: 334 raise self.interrupt 335 if not self.handle_request(): 336 break 337 self.server_close() 338 start = serve_forever 339 340 def handle_request(self): 341 """Override handle_request to enqueue requests rather than handle 342 them synchronously. Return 1 by default, 0 to shutdown the 343 server.""" 344 try: 345 request, client_address = self.get_request() 346 except socket.error, e: 347 return 1 348 self._requestQueue.put((request, client_address)) 349 return 1 350 351 def get_request(self): 352 # With Python 2.3 it seems that an accept socket in timeout 353 # (nonblocking) mode results in request sockets that are also set 354 # in nonblocking mode. Since that doesn't play well with makefile() 355 # (where wfile and rfile are set in SocketServer.py) we explicitly 356 # set the request socket to blocking 357 358 request, client_address = self.socket.accept() 359 if hasattr(request,'setblocking'): 360 request.setblocking(1) 361 return request, client_address 317 self.workerThreads = [] 318 stop = shutdown = server_close 319 320 321 _SHUTDOWNREQUEST = (0,0) 322 323 class ServerThread(threading.Thread): 324 325 def __init__(self, RequestHandlerClass, requestQueue, server): 326 self.server = server 327 self.ready = False 328 threading.Thread.__init__(self) 329 self.RequestHandlerClass = RequestHandlerClass 330 self.requestQueue = requestQueue 331 332 def run(self): 333 try: 334 self.ready = True 335 while 1: 336 request, client_address = self.requestQueue.get() 337 if (request, client_address) == _SHUTDOWNREQUEST: 338 return 339 try: 340 try: 341 self.RequestHandlerClass(request, client_address, self) 342 except (KeyboardInterrupt, SystemExit): 343 raise 344 except: 345 cherrypy.log(_cputil.formatExc()) 346 finally: 347 request.close() 348 except (KeyboardInterrupt, SystemExit), exc: 349 self.server.interrupt = exc 362 350 363 351 … … 366 354 367 355 # Select the appropriate server based on config options 368 sockFile = cherrypy.config.get('server.socketFile') 369 threadPool = cherrypy.config.get('server.threadPool') 370 if threadPool > 1 and not sockFile: 356 if cherrypy.config.get('server.threadPool', 1) > 1: 371 357 ServerClass = PooledThreadServer 372 358 else: trunk/cherrypy/_cphttptools.py
r838 r843 7 7 8 8 import cherrypy 9 from cherrypy import _cputil, _cpcgifs , _cpwsgiserver9 from cherrypy import _cputil, _cpcgifs 10 10 from cherrypy.filters import applyFilters 11 11 from cherrypy.lib import cptools, httptools … … 219 219 environ=methenv, 220 220 keep_blank_values=1) 221 except _cpwsgiserver.MaxSizeExceeded:221 except httptools.MaxSizeExceeded: 222 222 # Post data is too big 223 223 raise cherrypy.HTTPError(413) trunk/cherrypy/_cpwsgi.py
r838 r843 3 3 import sys 4 4 import cherrypy 5 from cherrypy import _cputil 6 from cherrypy. _cpwsgiserver import CherryPyWSGIServer as server5 from cherrypy import _cputil, _cpwsgiserver 6 from cherrypy.lib import httptools 7 7 8 8 … … 108 108 109 109 110 111 110 # Server components. 112 111 113 class WSGIServer(server): 112 113 class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 114 115 def __init__(self, socket, addr, server): 116 _cpwsgiserver.HTTPRequest.__init__(self, socket, addr, server) 117 mhs = int(cherrypy.config.get('server.maxRequestHeaderSize', 118 500 * 1024)) 119 self.rfile = httptools.SizeCheckWrapper(self.rfile, mhs) 120 121 def parse_request(self): 122 try: 123 _cpwsgiserver.HTTPRequest.parse_request(self) 124 except httptools.MaxSizeExceeded: 125 msg = "Request Entity Too Large" 126 proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0") 127 self.wfile.write("%s 413 %s\r\n" % (proto, msg)) 128 self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg)) 129 self.wfile.write(msg) 130 self.wfile.flush() 131 self.ready = False 132 133 tb = _cputil.formatExc() 134 cherrypy.log(tb) 135 else: 136 if self.ready: 137 # Request header is parsed 138 # We prepare the SizeCheckWrapper for the request body 139 self.rfile.bytes_read = 0 140 path = self.environ["SCRIPT_NAME"] 141 if path == "*": 142 path = "global" 143 else: 144 path = "/" + path 145 mbs = int(cherrypy.config.get('server.maxRequestBodySize', 146 100 * 1024 * 1024, path=path)) 147 self.rfile.maxlen = mbs 148 149 150 class WSGIServer(_cpwsgiserver.CherryPyWSGIServer): 114 151 115 152 """Wrapper for _cpwsgiserver.CherryPyWSGIServer. … … 121 158 """ 122 159 160 RequestHandlerClass = CPHTTPRequest 161 123 162 def __init__(self): 124 163 conf = cherrypy.config.get 125 server.__init__(self, 126 (conf("server.socketHost"), conf("server.socketPort")), 127 wsgiApp, 128 conf("server.threadPool"), 129 conf("server.socketHost"), 130 config = cherrypy.config 131 ) 164 165 sockFile = cherrypy.config.get('server.socketFile') 166 if sockFile: 167 bind_addr = sockFile 168 else: 169 bind_addr = (conf("server.socketHost"), conf("server.socketPort")) 170 171 s = _cpwsgiserver.CherryPyWSGIServer 172 s.__init__(self, bind_addr, wsgiApp, 173 conf("server.threadPool"), 174 conf("server.socketHost"), 175 request_queue_size = conf('server.socketQueueSize'), 176 ) 177 trunk/cherrypy/_cpwsgiserver.py
r825 r843 13 13 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 14 14 15 class MaxSizeExceeded(Exception):16 pass17 18 class SizeCheckWrapper(object):19 """ Wrapper around the rfile object. For each data reading method,20 it reads the data but it checks that the size of the data doesn't21 exceed a certain limit22 """23 def __init__(self, rfile, maxlen):24 self.rfile = rfile25 self.maxlen = maxlen26 self.bytes_read = 027 def _check_length(self):28 if self.maxlen and self.bytes_read > self.maxlen:29 raise MaxSizeExceeded()30 def read(self, size = None):31 data = self.rfile.read(size)32 self.bytes_read += len(data)33 self._check_length()34 return data35 def readline(self, size = None):36 if size is not None:37 data = self.rfile.readline(size)38 self.bytes_read += len(data)39 self._check_length()40 return data41 42 # User didn't specify a size ...43 # We read the line in chunks to make sure it's not a 100MB line !44 res = []45 while True:46 data = self.rfile.readline(256)47 self.bytes_read += len(data)48 self._check_length()49 res.append(data)50 if len(data) < 256:51 return ''.join(res)52 def close(self):53 self.rfile.close()54 55 def __iter__(self):56 return self.rfile57 58 def next(self):59 data = self.rfile.next()60 self.bytes_read += len(data)61 self._check_length()62 ## Normally the next method must raise StopIteration when it63 ## fails but CP expects MaxSizeExceeded64 ## try:65 ## self._check_length()66 ## except:67 ## raise StopIteration()68 return data69 15 70 16 class HTTPRequest(object): 17 18 stderr = sys.stderr 19 bufsize = -1 20 71 21 def __init__(self, socket, addr, server): 72 22 self.socket = socket … … 79 29 self.outheaders = [] 80 30 self.outheaderkeys = [] 81 self.rfile = self.socket.makefile("r", self.server.bufsize) 82 if self.server.config: 83 mhs = self.server.config.get( 84 'server.maxRequestHeaderSize', 85 500 * 1024) # 500KB by default 86 self.rfile = SizeCheckWrapper(self.rfile, mhs) 87 self.wfile = self.socket.makefile("w", self.server.bufsize) 31 self.rfile = self.socket.makefile("r", self.bufsize) 32 self.wfile = self.socket.makefile("w", self.bufsize) 88 33 self.sent_headers = False 89 34 … … 94 39 self.environ["wsgi.url_scheme"] = "http" 95 40 self.environ["wsgi.input"] = self.rfile 96 self.environ["wsgi.errors"] = self.s erver.stderr41 self.environ["wsgi.errors"] = self.stderr 97 42 self.environ["wsgi.multithread"] = True 98 43 self.environ["wsgi.multiprocess"] = False … … 116 61 self.environ["SERVER_PROTOCOL"] = version 117 62 self.environ["SERVER_NAME"] = self.server.server_name 118 self.environ["SERVER_PORT"] = str(self.server.bind_addr[1]) 119 # optional values 120 self.environ["REMOTE_HOST"] = self.addr[0] 121 self.environ["REMOTE_ADDR"] = self.addr[0] 122 self.environ["REMOTE_PORT"] = str(self.addr[1]) 63 if isinstance(self.server.bind_addr, basestring): 64 # AF_UNIX. This isn't really allowed by WSGI, which doesn't 65 # address unix domain sockets. But it's better than nothing. 66 self.environ["SERVER_PORT"] = "" 67 else: 68 self.environ["SERVER_PORT"] = str(self.server.bind_addr[1]) 69 # optional values 70 self.environ["REMOTE_HOST"] = self.addr[0] 71 self.environ["REMOTE_ADDR"] = self.addr[0] 72 self.environ["REMOTE_PORT"] = str(self.addr[1]) 123 73 # then all the http headers 124 74 headers = mimetools.Message(self.rfile) … … 129 79 self.environ[envname] = v 130 80 self.ready = True 131 132 # Request header is parsed133 # We prepare the SizeCheckWrapper for the request body134 if self.server.config:135 mbs = self.server.config.get(136 'server.maxRequestBodySize',137 100 * 1024 * 1024, # 100MB by default138 path = path)139 self.rfile.bytes_read = 0140 self.rfile.maxlen = mbs141 81 142 82 def start_response(self, status, headers, exc_info = None): … … 224 164 else: 225 165 raise 226 except MaxSizeExceeded:227 str = "Request Entity Too Large"228 proto = request.environ.get("SERVER_PROTOCOL", "HTTP/1.0")229 request.wfile.write("%s 413 %s\r\n" % (proto, str))230 request.wfile.write("Content-Length: %s\r\n\r\n" % len(str))231 request.wfile.write(str)232 request.wfile.flush()233 166 except (KeyboardInterrupt, SystemExit), exc: 234 167 self.server.interrupt = exc … … 246 179 ready = False 247 180 interrupt = None 181 RequestHandlerClass = HTTPRequest 248 182 249 183 def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 250 stderr=sys.stderr, bufsize=-1, max=-1, 251 config = None): 184 max=-1, request_queue_size=5): 252 185 ''' 253 186 be careful w/ max … … 257 190 self.bind_addr = bind_addr 258 191 self.numthreads = numthreads or 1 259 self.config = config 260 if server_name: 261 self.server_name = server_name 262 else: 263 self.server_name = socket.gethostname() 264 self.stderr = stderr 265 self.bufsize = bufsize 192 if not server_name: 193 server_name = socket.gethostname() 194 self.server_name = server_name 195 self.request_queue_size = request_queue_size 266 196 self._workerThreads = [] 267 197 268 198 def start(self): 269 ''' 270 run the server forever 271 ''' 199 """Run the server forever.""" 272 200 # We don't have to trap KeyboardInterrupt or SystemExit here, 273 201 # because cherrpy.server already does so, calling self.stop() for us. 274 202 # If you're using this server with another framework, you should 275 203 # trap those exceptions in whatever code block calls start(). 276 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP) 277 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 278 self.socket.bind(self.bind_addr) 204 205 # Select the appropriate socket 206 if isinstance(self.bind_addr, basestring): 207 # AF_UNIX socket 208 209 # So we can reuse the socket... 210 try: os.unlink(self.bind_addr) 211 except: pass 212 213 # So everyone can access the socket... 214 try: os.chmod(self.bind_addr, 0777) 215 except: pass 216 217 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 218 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 219 self.socket.bind(self.bind_addr) 220 else: 221 # AF_INET or AF_INET6 socket 222 # Get the correct address family for our host (allows IPv6 addresses) 223 host, port = self.bind_addr 224 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 225 socket.SOCK_STREAM): 226 af, socktype, proto, canonname, sa = res 227 try: 228 self.socket = socket.socket(af, socktype, proto) 229 self.socket.setsockopt(socket.SOL_SOCKET, 230 socket.SO_REUSEADDR, 1) 231 self.socket.bind(self.bind_addr) 232 except socket.error, msg: 233 if self.socket: 234 self.socket.close() 235 self.socket = None 236 continue 237 break 238 239 if not self.socket: 240 raise socket.error, msg 241 279 242 # Timeout so KeyboardInterrupt can be caught on Win32 280 243 self.socket.settimeout(1) 281 self.socket.listen( 5)244 self.socket.listen(self.request_queue_size) 282 245 283 246 # Create worker threads … … 301 264 if hasattr(s, 'setblocking'): 302 265 s.setblocking(1) 303 request = HTTPRequest(s, addr, self)266 request = self.RequestHandlerClass(s, addr, self) 304 267 self.requests.put(request) 305 # optimized version follows306 #self.requests.put(HTTPRequest(*self.socket.accept()))307 268 except socket.timeout: 308 269 # The only reason for the timeout in start() is so we can trunk/cherrypy/lib/httptools.py
r826 r843 410 410 return None 411 411 return header_elements(key, h) 412 413 414 class MaxSizeExceeded(Exception): 415 pass 416 417 class SizeCheckWrapper(object): 418 """ Wrapper around an rfile object. For each data reading method, 419 it reads the data but it checks that the size of the data doesn't 420 exceed a certain limit 421 """ 422 def __init__(self, rfile, maxlen): 423 self.rfile = rfile 424 self.maxlen = maxlen 425 self.bytes_read = 0 426 427 def _check_length(self): 428 if self.maxlen and self.bytes_read > self.maxlen: 429 raise MaxSizeExceeded() 430 431 def read(self, size = None): 432 data = self.rfile.read(size) 433 self.bytes_read += len(data) 434 self._check_length() 435 return data 436 437 def readline(self, size = None): 438 if size is not None: 439 data = self.rfile.readline(size) 440 self.bytes_read += len(data) 441 self._check_length() 442 return data 443 444 # User didn't specify a size ... 445 # We read the line in chunks to make sure it's not a 100MB line ! 446 res = [] 447 while True: 448 data = self.rfile.readline(256) 449 self.bytes_read += len(data) 450 self._check_length() 451 res.append(data) 452 if len(data) < 256: 453 return ''.join(res) 454 455 def close(self): 456 self.rfile.close() 457 458 def __iter__(self): 459 return self.rfile 460 461 def next(self): 462 data = self.rfile.next() 463 self.bytes_read += len(data) 464 self._check_length() 465 ## Normally the next method must raise StopIteration when it 466 ## fails but CP expects MaxSizeExceeded 467 ## try: 468 ## self._check_length() 469 ## except: 470 ## raise StopIteration() 471 return data trunk/cherrypy/test/test_core.py
r838 r843 834 834 835 835 httpcls = cherrypy.server.httpserverclass 836 if httpcls and httpcls.__name__ == "WSGIServer":836 if httpcls: 837 837 cherrypy.config.update({'server.maxRequestHeaderSize': 10}) 838 838 self.getPage("/maxrequestsize/index") 839 839 self.assertStatus("413 Request Entity Too Large") 840 self.assert Body("Request Entity Too Large")840 self.assertInBody("Request Entity Too Large") 841 841 cherrypy.config.update({'server.maxRequestHeaderSize': 0}) 842 842 … … 855 855 856 856 httpcls = cherrypy.server.httpserverclass 857 if httpcls and httpcls.__name__ == "WSGIServer":857 if httpcls: 858 858 cherrypy.config.update({ 859 859 '%s/maxrequestsize' % helper.vroot: {'server.maxRequestBodySize': 3}})

