读webpy源码-请求响应概述

web.py源码主体流程认识
  • 简要内部流程图
    这里写图片描述
  • 1.上述代码是项目启动应用的入口:

if __name__=="__main__":
 app = web.application(urls, globals())
 #session_hook为自定义方法(hook)
 app.add_processor(web.loadhook(session_hook)) 
 #request_unlock_hook为自定义方法(hook)
 app.add_processor(web.unloadhook(request_unlock_hook))
 app.run() #点run方法进入源码
 
 

说明:可以点开run方法真正进入源码

—————— * 2.run方法源码:

def run(self, *middleware):
"""
Starts handling requests. If called in a CGI or FastCGI context, it will follow  that protocol. If called from the command line, it will start an HTTP server on the port named in the first command line argument, or, if there is no argument, on port 8080.
  `middleware` is a list of WSGI middleware which is applied to the resulting WSGI function.
"""
    return wsgi.runwsgi(self.wsgifunc(*middleware))

说明:可以点开wsgi.runwsgi方法进入源码

————– * 3.wsgi.runwsgi方法源码:

def runwsgi(func):
    """
    Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
    as appropriate based on context and `sys.argv`.
    """
    if os.environ.has_key('SERVER_SOFTWARE'): # cgi
        os.environ['FCGI_FORCE_CGI'] = 'Y'
    if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi
      or os.environ.has_key('SERVER_SOFTWARE')):
        return runfcgi(func, None)
    if 'fcgi' in sys.argv or 'fastcgi' in sys.argv:
        args = sys.argv[1:]
        if 'fastcgi' in args: args.remove('fastcgi')
        elif 'fcgi' in args: args.remove('fcgi')
        if args:
            return runfcgi(func, validaddr(args[0]))
        else:
            return runfcgi(func, None)
    if 'scgi' in sys.argv:
        args = sys.argv[1:]
        args.remove('scgi')
        if args:
            return runscgi(func, validaddr(args[0]))
        else:
            return runscgi(func)
    return httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))

说明:因为项目默认都是以httpserver方式启动,这里看httpserver.runsimple源码

—— * 4.httpserver.runsimple源码:
def runsimple(func, server_address=("0.0.0.0", 8080)):
    #说明,默认以8080端口启动
    """
    Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 
    The directory `static/` is hosted statically.
    [cp]: http://www.cherrypy.org
    """
    global server
    func = StaticMiddleware(func)
    func = LogMiddleware(func)
    server = WSGIServer(server_address, func)
    if server.ssl_adapter:
        print "https://%s:%d/" % server_address
    else:
        print "http://%s:%d/" % server_address
    try:
        server.start()
    except (KeyboardInterrupt, SystemExit):
        server.stop()
        server = None
    

说明:这里主要看两行代码WSGIServer(server_address, func)和server.start()

———— * 5.WSGIServer源码:

def WSGIServer(server_address, wsgi_app):
    """
    Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`.
    This function can be overwritten to customize the webserver or use a different webserver.
    """
    import wsgiserver

   """
   Default values of wsgiserver.ssl_adapters uses         cherrypy.wsgiserver
    prefix. Overwriting it make it work with web.wsgiserver.
    """
    wsgiserver.ssl_adapters = {
        'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
     'pyopenssl':'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
    }

    server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")

    def create_ssl_adapter(cert, key):

    # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo.
    # That doesn't work as not it is web.wsgiserver. 
    # Patching sys.modules temporarily to make it work.
        import types
        cherrypy = types.ModuleType('cherrypy')
        cherrypy.wsgiserver = wsgiserver
        sys.modules['cherrypy'] = cherrypy
        sys.modules['cherrypy.wsgiserver'] = wsgiserver
        from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
        adapter = pyOpenSSLAdapter(cert, key)
        # We are done with our work. Cleanup the patches.
        del sys.modules['cherrypy']
        del sys.modules['cherrypy.wsgiserver']
        return adapter

    # SSL backward compatibility
    if (server.ssl_adapter is None and
        getattr(server, 'ssl_certificate', None) and
        getattr(server, 'ssl_private_key', None)):

        server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key)
        server.nodelay = not sys.platform.startswith('java') 
    # TCP_NODELAY isn't supported on the JVM
    return server

说明:主要定义了一个CherryPyWSGIServer对象返回。
CherryPyWSGIServer的作用: A subclass of HTTPServer which calls a WSGI application. 它集成了HTTPServer,会启一个线程池,把从socket中获取request包装成httpconnect,然后扔给wsgi_app处理。


  • 5.1 CherryPyWSGIServer源码:

                     
    class CherryPyWSGIServer(HTTPServer):
    wsgi_version = (1, 0)
    
    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
        self.wsgi_app = wsgi_app
        self.gateway = wsgi_gateways[self.wsgi_version]
        self.bind_addr = bind_addr
        if not server_name:
            server_name = socket.gethostname()
        self.server_name = server_name
        self.request_queue_size = request_queue_size
        self.timeout = timeout
        self.shutdown_timeout = shutdown_timeout
        self.clear_stats()
    
    def _get_numthreads(self):
        return self.requests.min
    def _set_numthreads(self, value):
        self.requests.min = value
    numthreads = property(_get_numthreads, _set_numthreads)
    
    


    说明:最主要是看self.requests = ThreadPool(self, min=numthreads or 1, max=max),这里看到request是创建了一个线程池,后面会关联起来看是什么用的。

  • 6.server.start()源码:

"""Run the server forever."""
        # We don't have to trap KeyboardInterrupt or SystemExit here,
        # because cherrpy.server already does so, calling self.stop() for us.
        # If you're using this server with another framework, you should
        # trap those exceptions in whatever code block calls start().
        self._interrupt = None

        if self.software is None:
            self.software = "%s Server" % self.version

        # Select the appropriate socket
        if isinstance(self.bind_addr, basestring):
            # AF_UNIX socket

            # So we can reuse the socket...
            try:
                os.unlink(self.bind_addr)
            except:
                pass

            # So everyone can access the socket...
            try:
                os.chmod(self.bind_addr, 0o777)
            except:
                pass

            info = [
                (socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
        else:
            # AF_INET or AF_INET6 socket
            # Get the correct address family for our host (allows IPv6
            # addresses)
            host, port = self.bind_addr
            try:
                info = socket.getaddrinfo(
                    host, port, socket.AF_UNSPEC,
                    socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
            except socket.gaierror:
                if ':' in self.bind_addr[0]:
                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
                             0, "", self.bind_addr + (0, 0))]
                else:
                    info = [(socket.AF_INET, socket.SOCK_STREAM,
                             0, "", self.bind_addr)]

        self.socket = None
        msg = "No socket could be created"
        for res in info:
            af, socktype, proto, canonname, sa = res
            try:
                self.bind(af, socktype, proto)
            except socket.error, serr:
                msg = "%s -- (%s: %s)" % (msg, sa, serr)
                if self.socket:
                    self.socket.close()
                self.socket = None
                continue
            break
        if not self.socket:
            raise socket.error(msg)

   # Timeout so KeyboardInterrupt can be caught on Win32
        self.socket.settimeout(1)
        self.socket.listen(self.request_queue_size)

        # Create worker threads
        self.requests.start()

        self.ready = True
        self._start_time = time.time()
        while self.ready:
            try:
                self.tick()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                self.error_log("Error in HTTPServer.tick", level=logging.ERROR,
                               traceback=True)

            if self.interrupt:
                while self.interrupt is True:
   # Wait for self.stop() to complete. See _set_interrupt.
                    time.sleep(0.1)
                if self.interrupt:
                    raise self.interrupt

说明:
启动之前的request(self.requests = ThreadPool)线程;启动、监听socket;将socket信息包装成connection扔到request队列中。主要看self.tick()

  • 7.server.start()源码:

"""说明:接受一个新的connect,然后put到队列中"""
        try:
            s, addr = self.socket.accept()
            if self.stats['Enabled']:
                self.stats['Accepts'] += 1
            if not self.ready:
                return

        prevent_socket_inheritance(s)
        if hasattr(s, 'settimeout'):
            s.settimeout(self.timeout)

        makefile = CP_fileobject
        ssl_env = {}
        if self.ssl_adapter is not None:
           try:
              s, ssl_env = self.ssl_adapter.wrap(s)
              except NoSSLError:
              msg = ("The client sent a plain HTTP request, but this server only speaks HTTPS on this port.")
              buf = ["%s 400 Bad Request\r\n" % self.protocol,"Content-Length: %s\r\n" % len(msg),
                           "Content-Type: text/plain\r\n\r\n",msg]

              wfile = makefile(s._sock, "wb", DEFAULT_BUFFER_SIZE)
              try:
                  wfile.sendall("".join(buf))
                  except socket.error:
                     x = sys.exc_info()[1]
                     if x.args[0] not in socket_errors_to_ignore:
                            raise
                    return
                if not s:
                    return
                makefile = self.ssl_adapter.makefile
                if hasattr(s, 'settimeout'):
                    s.settimeout(self.timeout)

            conn = self.ConnectionClass(self, s, makefile)

            if not isinstance(self.bind_addr, basestring):
                if addr is None:  
                    if len(s.getsockname()) == 2:
                        addr = ('0.0.0.0', 0)
                    else:
                        addr = ('::', 0)
                conn.remote_addr = addr[0]
                conn.remote_port = addr[1]
            conn.ssl_env = ssl_env
            try:
                self.requests.put(conn)
            except queue.Full:
                conn.close()
                return
        except socket.timeout:
            return
        except socket.error:
            x = sys.exc_info()[1]
            if self.stats['Enabled']:
                self.stats['Socket Errors'] += 1
            if x.args[0] in socket_error_eintr:
                return
            if x.args[0] in socket_errors_nonblocking:
               return
            if x.args[0] in socket_errors_to_ignore:
                return
            raise

说明:从socket中接收信息,包装成connection扔到队列里。


上述简单罗列和简要说明了WSGI处理流程。后面逐一看看需要关心的重点细节.

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值