Archive for November 26th, 2007

web.py的HTTP研究

Monday, November 26th, 2007

 

web.py的HTTP研究

作者: gashero 日期: 2007-11-21 版本: web.py-0.22

 

目录

1   简介

我想要实现使用twisted作为HTTP服务器,而web.py作为后端的动态引擎的部署方式。从而实现高性能,又易于学习的一种开发方式。

2   一个基本的例子

见web.py首页上提供的例子,如下:

import web
urls=(
    '/(.*)','hello',
)
class hello:
    def GET(self,name):
        i=web.input(times=1)
        if not name:
            name='world'
        for c in xrange(int(i.times)):
            print 'Hello,',name+'!'
if __name__=="__main__":
    web.run(urls,globals())

可见一个web.py编写的应用包含一个url映射表,一些映射处理类,类按照请求方法命名的方法实现了对应的逻辑,最后包含一个启动服务器的语句,只要传入URL映射即可。

3   web.py所属文件简介

3.1   __init__.py

导入所有的下属模块,并且有个主函数会运行doctest测试用例。导入的模块全部分为两种,例如:

import utils
from utils import *

3.2   cheetah.py

98行代码实现了cheetah的接口规范化,最终还是要导入真正的Cheetah,只是这里提供的接口比较好用。如:

def render(template,terms=None,asTemplate=False,base=None,isString=False)

3.3   db.py

自动导入DBUtils作为数据库连接池并提供web.py友好的接口。有703行代码,貌似大了一点。

3.4   debugerror.py

以Django的模板提供了调试服务器运行时的错误信息展示。316行代码。

3.5   form.py

提供了简单的表单代码生成,和貌似有数据验证功能。提供了面向对象的组织结构。215行代码的。

3.6   http.py

269行代码。实现了HTTP的一些机制,比如cookie、缓存过期、重定向等等。好像是与httpserver.py没有什么太大的耦合,但是也要小心,看到一些web.header之类的设置,这岂不是全局的?

3.7   httpserver.py

224行代码。实现了一个简单的HTTP服务器。还是基于Python自带的那个SimpleHTTPServer模块写的,所以也是单线程的。这里一共就2个函数,分别是启动不同的服务器,不过貌似都是使用wsgi方式实现的。

3.8   net.py

155行代码。IP地址验证,URL验证等等。

3.9   request.py

153行代码。好像就是请求处理的入口了。同时这里也有个 run() 函数,貌似就是启动服务器的入口。

3.10   template.py

878行代码,貌似实现了一个比较像Cheetah的模板系统吧,里面有明确的代码显示,这里面做了很多代码解析的工作,至少是有词法解析器。

3.11   utils.py

796行代码。看来就是我以前一直期望的那个具有很多超牛功能的东西,如函数的执行缓存等等。

3.12   webapi.py

369行代码。提供了一些常用功能的函数接口,如 badrequest() 、 notfound() 、 gone() 等等,这样可以加快解决过程。另外像 setcookie() 、 cookies() 、 debug() 等等也是编程所必需的。

3.13   wsgi.py

54行代码。提供了3个函数,分别是 runfcgi() 、 runscgi() 、 runwsgi() 。分别提供3种不同的发布方式。

3.14   wsgiserver/

仅内含一个 __init__.py 文件,其实是从CherryPy搞来的WSGI服务器的代码。

4   HTTP处理流程

4.1   启动服务器

启动服务器的代码:

web.run(urls,globals())

实际上是调用 web.request 模块中的 run() 函数。代码如下:

def run(inp,fvars,*middleware):
    autoreload=http.reloader in middleware
    return wsgi.runwsgi(webapi.wsgifunc(webpyfunc(inp,fvars,autoreload),*middleware))

其中首先确定是否需要动态重新装入功能,然后就是把 webpyfunc() 的调用结果,就是一个函数对象传入 webapi.wsgifunc() 函数。 webapi.wsgifunc() 函数接受两个参数,一个是 webpyfunc() 的结果,另一个是中间件列表。

4.2   webpyfunc() 生成调用请求处理器的函数

在模块 web.request 中,声明如下:

def webpyfunc(inp,fvars,autoreload=False)

参数解释:

  • inp :可以是函数或一个tuple,用于URL映射
  • fvars :一个dict,存储可用的变量
  • autoreload :是否自动重装

完全按照例子当中执行时webpyfunc()函数的调用参数为:

webpyfunc(urls,globals(),False)

这样最终是返回的结果是:

return lambda: handle(urls,globals())

即便 autoreload=True 估计也是同样的结果,只是可以自动重新加载而已。

5   启动的HTTP服务器

经分析,默认的服务器最终启动的是 web.wsgi 模块中 runwsgi() 函数中的如下语句:

return httpserver.runsimple(func,validip(listget(sys.argv,1,'')))

这个函数实际上内部居然还包含两个类的定义,一个作为静态文件服务的,叫 StaticApp ,另外一个用作动态的,叫 WSGIWrapper

6   总结

真不知道wsgi为什么这么重要,以至于我接触的几个框架基本上都是用wsgi来实现底层。每每让我无法下手修改其行为。django算是走的距离核心最近的一个了,而其他的基本上还没有摸到边。

总之,还是自己写那个htmid吧,自己写的终归了解。