# -*- coding: utf-8 -*-""" werkzeug.serving ~~~~~~~~~~~~~~~~ There are many ways to serve a WSGI application. While you're developing it you usually don't want a full blown webserver like Apache but a simple standalone one. From Python 2.5 onwards there is the `wsgiref`_ server in the standard library. If you're using older versions of Python you can download the package from the cheeseshop. However there are some caveats. Sourcecode won't reload itself when changed and each time you kill the server using ``^C`` you get an `KeyboardInterrupt` error. While the latter is easy to solve the first one can be a pain in the ass in some situations. The easiest way is creating a small ``start-myproject.py`` that runs the application:: #!/usr/bin/env python # -*- coding: utf-8 -*- from myproject import make_app from werkzeug.serving import run_simple app = make_app(...) run_simple('localhost', 8080, app, use_reloader=True) You can also pass it a `extra_files` keyword argument with a list of additional files (like configuration files) you want to observe. For bigger applications you should consider using `werkzeug.script` instead of a simple start file. :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details. :license: BSD, see LICENSE for more details."""import__future__importosimportsocketimportsysimportsignalfromurllibimportunquotefromweakrefimportproxyfromSocketServerimportThreadingMixIn,ForkingMixInfromBaseHTTPServerimportHTTPServer,BaseHTTPRequestHandlerimportwerkzeugfromwerkzeug._internalimport_logfromwerkzeug.exceptionsimportInternalServerErrorifhasattr(__future__,'with_statement'):fromwebserver_sslimportload_ssl_context, \
is_ssl_error, \
generate_adhoc_ssl_context, \
_SSLConnectionFixclassWSGIRequestHandler(BaseHTTPRequestHandler,object):"""A request handler that implements WSGI dispatching."""@propertydefserver_version(self):return'Werkzeug/'+werkzeug.__version__defmake_environ(self):if'?'inself.path:path_info,query=self.path.split('?',1)else:path_info=self.pathquery=''server_proxy=proxy(self.server)defshutdown_server():server_proxy.shutdown_signal=Trueurl_scheme=self.server.ssl_contextisNoneand'http'or'https'environ={'wsgi.version':(1,0),'wsgi.url_scheme':url_scheme,'wsgi.input':self.rfile,'wsgi.errors':sys.stderr,'wsgi.multithread':self.server.multithread,'wsgi.multiprocess':self.server.multiprocess,'wsgi.run_once':False,'werkzeug.server.shutdown':shutdown_server,'SERVER_SOFTWARE':self.server_version,'REQUEST_METHOD':self.command,'SCRIPT_NAME':'','PATH_INFO':unquote(path_info),'QUERY_STRING':query,'CONTENT_TYPE':self.headers.get('Content-Type',''),'CONTENT_LENGTH':self.headers.get('Content-Length',''),'REMOTE_ADDR':self.client_address[0],'REMOTE_PORT':self.client_address[1],'SERVER_NAME':self.server.server_address[0],'SERVER_PORT':str(self.server.server_address[1]),'SERVER_PROTOCOL':self.request_version}forkey,valueinself.headers.items():key='HTTP_'+key.upper().replace('-','_')ifkeynotin('HTTP_CONTENT_TYPE','HTTP_CONTENT_LENGTH'):environ[key]=valuereturnenvirondefrun_wsgi(self):app=self.server.appenviron=self.make_environ()headers_set=[]headers_sent=[]self_proxy=proxy(self)defwrite(data):assertheaders_set,'write() before start_response'ifnotheaders_sent:status,response_headers=headers_sent[:]=headers_setcode,msg=status.split(None,1)self_proxy.send_response(int(code),msg)header_keys=set()forkey,valueinresponse_headers:self_proxy.send_header(key,value)key=key.lower()header_keys.add(key)if'content-length'notinheader_keys:self_proxy.close_connection=Trueself_proxy.send_header('Connection','close')if'server'notinheader_keys:self_proxy.send_header('Server',self.version_string())if'date'notinheader_keys:self_proxy.send_header('Date',self.date_time_string())self_proxy.end_headers()asserttype(data)isstr,'applications must write bytes'self_proxy.wfile.write(data)self_proxy.wfile.flush()defstart_response(status,response_headers,exc_info=None):ifexc_info:try:ifheaders_sent:raiseexc_info[0],exc_info[1],exc_info[2]finally:exc_info=Noneelifheaders_set:raiseAssertionError('Headers already set')headers_set[:]=[status,response_headers]returnwritedefexecute(app):application_iter=app(environ,start_response)try:fordatainapplication_iter:write(data)# make sure the headers are sentifnotheaders_sent:write('')finally:ifhasattr(application_iter,'close'):application_iter.close()application_iter=Nonetry:execute(app)except(socket.error,socket.timeout),e:self.connection_dropped(e,environ)exceptException:ifself.server.passthrough_errors:raisefromwerkzeug.debug.tbtoolsimportget_current_tracebacktraceback=get_current_traceback(ignore_system_exceptions=True)try:# if we haven't yet sent the headers but they are set# we roll back to be able to set them again.ifnotheaders_sent:delheaders_set[:]execute(InternalServerError())exceptException:passself.server.log('error','Error on request:\n%s',traceback.plaintext)defhandle(self):"""Handles a request ignoring dropped connections."""rv=Nonetry:rv=BaseHTTPRequestHandler.handle(self)except(socket.error,socket.timeout),e:self.connection_dropped(e)exceptException:ifself.server.ssl_contextisNoneornotis_ssl_error():raiseifself.server.shutdown_signal:self.initiate_shutdown()returnrvdefinitiate_shutdown(self):"""A horrible, horrible way to kill the server for Python 2.6 and later. It's the best we can do. """# Windows does not provide SIGKILL, go with SIGTERM then.sig=getattr(signal,'SIGKILL',signal.SIGTERM)# reloader activeifos.environ.get('WERKZEUG_RUN_MAIN')=='true':os.kill(os.getpid(),sig)# python 2.7self.server._BaseServer__shutdown_request=True# python 2.6self.server._BaseServer__serving=Falsedefconnection_dropped(self,error,environ=None):"""Called if the connection was closed by the client. By default nothing happens. """defhandle_one_request(self):"""Handle a single HTTP request."""self.raw_requestline=self.rfile.readline()ifnotself.raw_requestline:self.close_connection=1elifself.parse_request():returnself.run_wsgi()defsend_response(self,code,message=None):"""Send the response header and log the response code."""self.log_request(code)ifmessageisNone:message=codeinself.responsesandself.responses[code][0]or''ifself.request_version!='HTTP/0.9':self.wfile.write("%s%d%s\r\n"%(self.protocol_version,code,message))defversion_string(self):returnBaseHTTPRequestHandler.version_string(self).strip()defaddress_string(self):returnself.client_address[0]deflog_request(self,code='-',size='-'):self.log('info','"%s" %s%s',self.requestline,code,size)deflog_error(self,*args):self.log('error',*args)deflog_message(self,format,*args):self.log('info',format,*args)deflog(self,type,message,*args):_log(type,'%s - - [%s] %s\n'%(self.address_string(),self.log_date_time_string(),message%args))classBaseWSGIServer(HTTPServer,object):"""Simple single-threaded, single-process WSGI server."""multithread=Falsemultiprocess=Falserequest_queue_size=128def__init__(self,server_socket,app,handler=None,passthrough_errors=False,ssl_context=None):ifhandlerisNone:handler=WSGIRequestHandlerself.server_socket=server_socketHTTPServer.__init__(self,('localhost',4000),handler)self.app=appself.passthrough_errors=passthrough_errorsself.shutdown_signal=Falseifssl_contextisnotNone:try:fromOpenSSLimporttsafeexceptImportError:raiseTypeError('SSL is not available if the OpenSSL ''library is not installed.')ifisinstance(ssl_context,tuple):ssl_context=load_ssl_context(*ssl_context)ifssl_context=='adhoc':ssl_context=generate_adhoc_ssl_context()self.socket=tsafe.Connection(ssl_context,self.socket)self.ssl_context=ssl_contextelse:self.ssl_context=Nonedefserver_bind(self):self.socket=self.server_socketself.server_address=self.socket.getsockname()deflog(self,type,message,*args):_log(type,message,*args)defserve_forever(self):self.shutdown_signal=Falsetry:_log('info',' * Ready')HTTPServer.serve_forever(self)exceptKeyboardInterrupt:passdefhandle_error(self,request,client_address):ifself.passthrough_errors:raiseelse:returnHTTPServer.handle_error(self,request,client_address)defget_request(self):con,info=self.socket.accept()ifself.ssl_contextisnotNone:con=_SSLConnectionFix(con)returncon,infoclassThreadedWSGIServer(ThreadingMixIn,BaseWSGIServer):"""A WSGI server that does threading."""multithread=TrueclassForkingWSGIServer(ForkingMixIn,BaseWSGIServer):"""A WSGI server that does forking."""multiprocess=Truedef__init__(self,server_socket,app,processes=40,handler=None,passthrough_errors=False,ssl_context=None):BaseWSGIServer.__init__(self,server_socket,app,handler,passthrough_errors,ssl_context)self.max_children=processesdefmake_server(server_socket,app=None,threaded=False,processes=1,request_handler=None,passthrough_errors=False,ssl_context=None):"""Create a new server instance that is either threaded, or forks or just processes one request after another. """ifthreadedandprocesses>1:raiseValueError("cannot have a multithreaded and ""multi process server.")elifthreaded:returnThreadedWSGIServer(server_socket,app,request_handler,passthrough_errors,ssl_context)elifprocesses>1:returnForkingWSGIServer(server_socket,app,processes,request_handler,passthrough_errors,ssl_context)else:returnBaseWSGIServer(server_socket,app,request_handler,passthrough_errors,ssl_context)defrun_simple(server_socket,application,use_debugger=False,use_evalex=True,extra_files=None,threaded=False,processes=1,request_handler=None,static_files=None,passthrough_errors=False,ssl_context=None):"""Start an application using wsgiref and with an optional reloader. This wraps `wsgiref` to fix the wrong default reporting of the multithreaded WSGI variable and adds optional multithreading and fork support. .. versionadded:: 0.5 `static_files` was added to simplify serving of static files as well as `passthrough_errors`. .. versionadded:: 0.6 support for SSL was added. .. versionadded:: 0.8 Added support for automatically loading a SSL context from certificate file and private key. :param socket: The socket for accpeting connections :param application: the WSGI application to execute :param use_debugger: should the werkzeug debugging system be used? :param use_evalex: should the exception evaluation feature be enabled? :param extra_files: a list of files the reloader should watch additionally to the modules. For example configuration files. :param threaded: should the process handle each request in a separate thread? :param processes: number of processes to spawn. :param request_handler: optional parameter that can be used to replace the default one. You can use this to replace it with a different :class:`~BaseHTTPServer.BaseHTTPRequestHandler` subclass. :param static_files: a dict of paths for static files. This works exactly like :class:`SharedDataMiddleware`, it's actually just wrapping the application in that middleware before serving. :param passthrough_errors: set this to `True` to disable the error catching. This means that the server will die on errors but it can be useful to hook debuggers in (pdb etc.) :param ssl_context: an SSL context for the connection. Either an OpenSSL context, a tuple in the form ``(cert_file, pkey_file)``, the string ``'adhoc'`` if the server should automatically create one, or `None` to disable SSL (which is the default). """ifuse_debugger:fromwerkzeug.debugimportDebuggedApplicationapplication=DebuggedApplication(application,use_evalex)ifstatic_files:fromwerkzeug.wsgiimportSharedDataMiddlewareapplication=SharedDataMiddleware(application,static_files)make_server(server_socket,application,threaded,processes,request_handler,passthrough_errors,ssl_context).serve_forever()