tornado源码分析(五): webApplication

web这个文件也算是tornado中比较大的一个文件了, 主要是RequestHandler以及继承他的子handler和Application
我们就先从Application说起, Application这个类是作为回调函数传递给HttpServer的, 至于怎么回调, 当然是执行__call__了
先来看看结构:

很简单 主要的方法当然是__call__
先来看看构造函数:
#handlers不用说了吧, 用过tornado的都知道, default_host可以实现vhost功能, 使用add_handlers就可以
#类似这样
#        application.add_handlers(r"www\.myhost\.com", [
 #           (r"/article/([0-9]+)", ArticleHandler),
#        ])
#这个transforms是编码方式, 其实就是开不开启gzip, 如果有nginx的话 可能会用nginx的gzip
def __init__(self, handlers=None, default_host="", transforms=None,
                 wsgi=False, **settings):
        if transforms is None:
            self.transforms = []
            if settings.get("gzip"):
                self.transforms.append(GZipContentEncoding)
            self.transforms.append(ChunkedTransferEncoding)
        else:
            self.transforms = transforms
        #存储host和对应handlers
        self.handlers = []                       #host和handlers
        self.named_handlers = {}           #reverse_url时候使用的(这部分功能其实并没实现)
        self.default_host = default_host   #vhost设置
        self.settings = settings                #settings, ui_modules就不说了
        self.ui_modules = {}                     
        self.ui_methods = {}
        self._wsgi = wsgi                       #是否是wsgi模式
        self._load_ui_modules(settings.get("ui_modules", {}))
        self._load_ui_methods(settings.get("ui_methods", {}))
        if self.settings.get("static_path"):
            path = self.settings["static_path"]
            handlers = list(handlers or [])
            static_url_prefix = settings.get("static_url_prefix",
                                             "/static/")
            handlers = [                                           #默认的handlers   static_url_prefix是静态文件的url开头
                (re.escape(static_url_prefix) + r"(.*)", StaticFileHandler,
                 dict(path=path)),
                (r"/(favicon\.ico)", StaticFileHandler, dict(path=path)),
                (r"/(robots\.txt)", StaticFileHandler, dict(path=path)),
            ] + handlers
        if handlers: self.add_handlers(".*$", handlers)

        # Automatically reload modified modules
        if self.settings.get("debug") and not wsgi:
            import autoreload
            autoreload.start()

  

再来看看add_handlers函数:
def add_handlers(self, host_pattern, host_handlers):
        """Appends the given handlers to our handler list."""
        if not host_pattern.endswith("$"):
            host_pattern += "$"
        handlers = []
        # The handlers with the wildcard host_pattern are a special
        # case - they're added in the constructor but should have lower
        # precedence than the more-precise handlers added later.
        # If a wildcard handler group exists, it should always be last
        # in the list, so insert new groups just before it.
        #默认是匹配所有的host, 当手动调用add_handlers时, 会把新的host对应的handlers插入到默认的host之前
        if self.handlers and self.handlers[-1][0].pattern == '.*$':
            self.handlers.insert(-1, (re.compile(host_pattern), handlers))
        else:
            self.handlers.append((re.compile(host_pattern), handlers))

        #给对应的域名添加对应的RequestHandler
        for spec in host_handlers:
            if type(spec) is type(()):
                assert len(spec) in (2, 3)
                pattern = spec[0]
                handler = spec[1]
                if len(spec) == 3:
                    kwargs = spec[2]
                else:
                    kwargs = {}
                spec = URLSpec(pattern, handler, kwargs)
            handlers.append(spec)
            #name是做什么, 这个研究了半天发现是用来reverse_url的 不过一直都没有实现
            # 因为构造URLSpec的时候name作为第四个参数, 而前面已经过滤掉了
            if spec.name:
                if spec.name in self.named_handlers:
                    logging.warning(
                        "Multiple handlers named %s; replacing previous value",
                        spec.name)
                self.named_handlers[spec.name] = spec

  

然后再看看__call__:
#这个函数的功能就是寻找对应的host和对应的hander然后创建一个新的handler
def __call__(self, request):
        """Called by HTTPServer to execute the request."""
        #如果设置了gzip, 可能会有两个transform, chunk和gzip, chunk是分块传输编码, 具体可以看一下rfc2616, gzip就不用说了
        #默认的是用chunk, 如果设置了gzip还会在chunk的方式上使用gzip压缩一下
        transforms = [t(request) for t in self.transforms]
        handler = None
        args = []
        kwargs = {}
        handlers = self._get_host_handlers(request)
        if not handlers:
            handler = RedirectHandler(
                request, "http://" + self.default_host + "/")
        else:
            for spec in handlers:
                match = spec.regex.match(request.path)
                if match:
                    # None-safe wrapper around urllib.unquote to handle
                    # unmatched optional groups correctly
                    def unquote(s):
                        if s is None: return s
                        return urllib.unquote(s)
                    #找到对应的handler, 生成一个新的RequestHandler
                    handler = spec.handler_class(self, request, **spec.kwargs)
                    # Pass matched groups to the handler.  Since
                    # match.groups() includes both named and unnamed groups,
                    # we want to use either groups or groupdict but not both.
                    kwargs = dict((k, unquote(v))
                                  for (k, v) in match.groupdict().iteritems())
                    #groups和groupdict只能使用一个
                    if kwargs:
                        args = []
                    else:
                        args = [unquote(s) for s in match.groups()]
                    break
            if not handler:
                handler = ErrorHandler(self, request, 404)

        # In debug mode, re-compile templates and reload static files on every
        # request so you don't need to restart to see changes
        # 如果是debug可能需要重新编译模板
        if self.settings.get("debug"):
            if getattr(RequestHandler, "_templates", None):
              map(lambda loader: loader.reset(),
                  RequestHandler._templates.values())
            RequestHandler._static_hashes = {}

        handler._execute(transforms, *args, **kwargs)
        return handler

  

URLSpec是一个URL和RequestHandler对应的对象, 接受4个参数, 不过最后一个参数貌似过滤掉了, 所以就变成接受3个参数了,
url的正则, 对应的handler和初始化的参数. 当创建handler的时候 会把初始化参数传递进去
 

这里面主要就是webApplication和RequestHandler, 其他的handler都是继承的RequestHandler

在上面的代码中,__call__的倒数第二行handler._execute()会调用requesthandler中的_execute函数, 由于RequestHandler中的函数比较多,

所以先来看_execute这个方法:

#在Application中被调用的方法, 参数为匹配的URL的结果
    def _execute(self, transforms, *args, **kwargs):
        """Executes this request with the given output transforms."""
        self._transforms = transforms
        try:
            if self.request.method not in self.SUPPORTED_METHODS:
                raise HTTPError(405)
            # If XSRF cookies are turned on, reject form submissions without
            # the proper cookie
            if self.request.method == "POST" and \
               self.application.settings.get("xsrf_cookies"):
                self.check_xsrf_cookie()
            self.prepare()
            if not self._finished:
                #调用相对应的方法, 如果用使用asynchronous修饰, 则_auto_finsh为false不自动结束
                getattr(self, self.request.method.lower())(*args, **kwargs)
                if self._auto_finish and not self._finished:
                    self.finish()
        except Exception, e:
            self._handle_request_exception(e)

其实写到这里tornado的基础功能就差不多完事了, 其他的大概都是一些辅助功能的函数以后如果有时间慢慢来写

posted on 2013-04-23 16:41  nobutawoproduce  阅读(460)  评论(0)    收藏  举报