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的基础功能就差不多完事了, 其他的大概都是一些辅助功能的函数以后如果有时间慢慢来写