Twisted Web Development
Twisted Web接受(serve)实现了IResource接口的Python对象。
Main Concepts:
Site Objects
负责创建HTTPChannel实例来解析HTTP request,并且启动对象查找过程。
他们包含root Resource,root Resource代表的是当前站点的/这个URL.
Resource
Resource对象代表一个单独的URL片段。IResource接口描述的是一个Resource对象为了参与对象发布过程(the object publishing process)而必须实现的方法。
Resource trees
Resource树就是把Resource对象整理成一颗Resource树。从root resource对象开始,resource对象树定义了所有有效的URL。
.rpy scripts
.rpy脚本是tiwsted.web静态文件服务器要执行的脚本,跟CGI很像。不过,跟CGI不一样的是他们必须创建一个resource对象,这个对象会在URL被访问的时候渲染出去(be rendered)。
Resource rendering
Resource rendering发生在Twisted Web定位到(locates)叶节点的resource对象(a leaf Resource object)的时候。一个Resource要嘛返回一段HTML字符串,要嘛往request对象里面写数据。
Session
Session对象允许我们跨越多个request存储信息。使用我们服务器的每个独立浏览器都有一个唯一的Session实例。
Twisted Web Server通过Twisted Daemonizer启动,如下:
%twistd web
Site Objects
Site对象的服务类似于“监听HTTP请求的端口”和“主Resource对象(a root Resource object)”之间的胶水。
当使用twistd -n web --path /foo/bar/baz时,一个指定了主Resource的Site对象就创建出来了,它为指定的路径提供文件展示(下载)。
我们也可以手动创建一个Site实例,传入一个Resource对象(Simple()),这个Resource对象作为site的根提供服务(which will serve as the root of the site):
Resource objectsfrom twisted.web import server, resource from twisted.internet import reactor class Simple(resource.Resource): isLeaf = True def render_GET(self, request): return "<html>Hello, world!</html>" site = server.Site(Simple()) reactor.listenTCP(8080, site) reactor.run()
Resource对象代表站点(site)的一个单独的URL片段(segment)。在URL解析过程中,对当前Resource调用getChild可以得到下一个Resource对象。
当到达叶节点Resource,要嘛是因为没有更多的URL片断(segments)了要嘛是因为一个Resource的idLeaf属性被设置成True了。叶节点Resource通过调用render(request)来渲染(render)。细节见下面的“Resource Rendering”.
在Resource定位过程中,已经处理过的URL片段(URL segments)和那些还没处理过的分别在request.prepath和request.postpath中。
Resource通过查看request.prepath来确定它在URL树中什么位置,request.prepath是一个URL片段(segment)的list.
Resource通过查看request.postpath来确定在它之后哪些路径片断(which path segments)将被处理。
如果URL以斜线(slash)结尾,例如http://example.com/foo/bar/,最后的URL片断将是一个空串。因此,Resources可以通过上述方式知道他们收到的请求是否以斜线结尾。
下面是一个简单的Resource对象:
from twisted.web.resource import Resource class Hello(Resource): isLeaf = False #False才能让getChild被调用 def getChild(self, name, request): if name == '': return self return Resource.getChild(self, name, request) def render_GET(self, request): return "Hello, world! I am located at %r." % (request.prepath,) resource = Hello()
Resource Trees
可以用putChild把Resources整理(arrange)到树里面。putChild把一个Resource实例放入到另一个Resource实例中,使得它可被指定的path片段名使用:
root = Hello() root.putChild('fred', Hello()) root.putChild('bob', Hello())如果这个root resource是作为一个Site实例的root提供服务的,那么下面URL将变成有效的:http://example.com/ http://example.com/fred http://example.com/bob http://example.com/fred/ http://example.com/bob/
.rpy scripts
扩展名为.rpy的文件是Python脚本,把这些文件放在Twisted Web服务的目录下,通过web访问时这些文件会被执行。
一个.rpy脚本必须定义一个变量resource,这个变量是将要渲染(render)request的Resource对象。
.rpy文件对于快速开发和建模(prototyping)来说非常方便。因为他们在每一次web请求(request)时被执行,所以,在.rpy中定义一个Resource子类将会在刷新页面时使如下功能变得更简单:可视化(visible)的呈现我们class改变带来的结果(这个大句子不知道怎么翻译较好,反正意思就是说,把Resource的子类定义在一个.rpy文件中可以使得让新修改的关于Resource子类的代码自动生效变得更简单)。
from twisted.web.resource import Resource class MyResource(Resource): def render_GET(self, request): return "<html>Hello, world!</html>" resource = MyResource()但是,在Python模块中定义Resource子类是更好的主意。为了让模块的改变生效(visible),我们要嘛重启Python进程,要嘛reload模块:import myresource ##Comment out this line when finished debugging reload(myresource) resource = myresource.MyResource()创建一个Twisted Web Server来服务于一个目录是很简单的:% twistd -n web --path /Users/dsp/Sites
Resource rendering
Resource rendering发生在Twisted Web定位到一个叶节点Resource对象来处理web请求(request)的时候。一个Resource的render方法可以做各种事情来产生结果输出,这些输出会发送回去给浏览器:
Request encoders <用firebug和tcpdump跟踪,没发现数据传输有压缩啊,不懂这个是搞什么的>返回一个字符串
调用request.write("stuff"),想调多少次就调多少次,然后调用request.finish()并return server.NOT_DONE_YES(这是骗人的,因为实际上我们已经完成这个请求了,但是这样做还是正确的(脑残了?不明白啥意思...,直接看下面的例子吧))
请求一个Deferred,返回server.NOT_DONE_YET,然后晚点在Defferred的回调函数里面调用request.write("stuff")和request.finish()
class Hello(Resource): #isLeaf = True def getChild(self, name, request): if name == '': return self return Resource.getChild(self, name, request) def render_GET(self, request): request.write('Hello,world') request.write('</br>Hello,吓人的鸟') request.finish() return server.NOT_DONE_YET #return "Hello,World! I am located at %r. %r is followed." % (request.prepath,request.postpath)Resource类,一般是Resource类的子类,有一个方便的render的默认实现。它调用一个名为self.render_METHOD(这里的METHOD是用于请求这个resource的HTTP方法)的方法。例如:request_GET,request_POST,request_HEAD,等等。我们推荐大家继承Resource类来定义自己的resource类,然后实现render_METHOD方法,而不是render方法。注意,对于某些resource,request_POST=request_GET可能会在下述情形下需要:某人想要处理传给resource的参数,不论他们是使用GET(?foo=bar&baz=quux,等等)还是POST。
对于一个Resource,可以指定用EncodingResourceWrapper+传入一个存储encoder factories的list来对它做包装。当一个请求被处理并且返回一个encoder的时候,encoder factories会被调用。默认情况下,twisted提供了GzipEncoderFactory,GzipEncoderFactory是管理标准的gzip压缩的。我们可以这样使用它:
from twisted.web.server import Site, GzipEncoderFactory from twisted.web.resource import Resource, EncodingResourceWrapper from twisted.internet import reactor class Simple(Resource): isLeaf = True def render_GET(self, request): return "<html>Hello, world!</html>" resource = Simple() wrapped = EncodingResourceWrapper(resource, [GzipEncoderFactory()]) site = Site(wrapped) reactor.listenTCP(8080, site) reactor.run()在使用SSL服务、用户可以影响(influence)内容的Resources上使用压缩会导致信息泄露,所以对哪个resource使用request encoders要小心抉择。注意,在每次request中只有(一个)encoder可以被使用:第一个encoder factory返回一个将被使用的对象,所以指定他们的顺序很重要。
Session
Proxies and reverse proxiesHTTP是一个无状态协议;每次请求-应答(request-response)都被作为一个独立单元对待,有别于其他任何URL请求(distinguishable from any other request only by the URL requested)。随着九十年代中期Cookies的出现,通过给浏览器发送一个Cookie,动态web服务器(dynamic web servers)获得了区分来自不同浏览器会话(sessions)的请求(request)的能力。然后浏览器在对web server发起请求的时候会同时发送这个cookie,允许服务器跟踪哪个请求来自哪个浏览器会话(browser session)。
Twisted Web对这种浏览器跟踪行为(browser-tracking behavior)提供了一种抽象,名叫Session的对象。调用request.getSession()会检测会话的cookie(a session cookie)是否已经设置;如果没有,它创建一个唯一的session id,创建一个Session对象,存储在Site里,然后return它。如果一个session对象已经存在了,那么同一个session对象会被return.通过这种方式,我们可以把针对这个会话(session)的数据存储到session对象里面。
Proxy代理(proxy)是对在clients和其他server之间起中介作用的server的一个笼统的术语。
Twisted支持两种主要的代理变体(variants):代理(Proxy)和反向代理(ReverseProxy).
ReverseProxyResourceproxy把client发起的请求转送给目标server. Proxies一般位于内部网络中作为客户端或者位于互联网出口(Proxies typically sit on the internal network for a client or out on the internet,嘛意思嘛?),它有很多用途,包括缓存,包过滤,审计(auditing),和规避对网页访问的本地限制。
这里是一个简单但完整的web代理:
from twisted.web import proxy, http from twisted.internet import reactor class ProxyFactory(http.HTTPFactory): def buildProtocol(self, addr): return proxy.Proxy() reactor.listenTCP(8080, ProxyFactory()) reactor.run()运行这个代理,我们可以配置我们的web浏览器使用proxy_host_ip:8080作为代理。这样做以后,当浏览web的时候所有请求都会通过这个代理。(试了一下,在虚拟机centos6里面运行上述程序,然后在本机win7上设置firefox走centos6提供的代理,真的很好使呢!!)Proxy继承自http.HTTPChannel.每个对proxy的客户端请求会产生一个从proxy到目标server的ProxyRequest来代表客户端。ProxyRequest使用ProxyClientFactory来为连接创建一个ProxyClient protocol的实例。ProxyClient继承自http.HTTPClient. 我们可以继承ProxyRequest来定制请求该怎样处理或记日志。
反向代理代表一个客户端从其他server重新获取resources。反向代理一般位于server的内部网络中用于缓存,应用程序防火墙,和负载均衡。(代理外部网络上的主机,访问内部网络)
这里是一个基本的反向代理的例子:
from twisted.internet import reactor from twisted.web import proxy, server site = server.Site(proxy.ReverseProxyResource('www.yahoo.com', 80, '')) reactor.listenTCP(8080, site) reactor.run()把这个代理运行在本地,我们可以用浏览器访问http://localhost:8080,然后反向代理会代理我们对www.yahoo.com的连接。
Advanced Configuration在这个例子中我们用server.Site来直接服务一个ReverseProxyResource. 在twisted.web.proxy中当然也有一个ReverseProxy家族的类来映射哪些Proxy家族:
和Proxy一样,ReverseProxy继承自http.HTTPChannel. 每个对反向代理的客户端请求会产生一个对目标server的ReverseProxyRequest. 和ProxyRequest一样,ReverseProxyRequest使用ProxyClientFactory来为连接创建一个ProxyClient protocol的实例。
其他代理和反向代理的例子可以在Twisted Web examples(http://twistedmatrix.com/documents/13.0.0/web/howto/using-twistedweb.html)中找到。
Adding ChildrenTwisted Web的non-trivial配置是用Python的配置文件来实现的。这是一个python片段,它建立一个名为application的变量。一般来说,twisted.application.internet.TCPServer实例用来使得应用程序在一个TCP端口(80,如果需要直接的web服务)做监听,监听者是一个twisted.web.server.Site. 结果文件可以用twistd -y来运行。或者一个reactor对象可以直接用来产生一个可执行的脚本。
from twisted.application import internet, service from twisted.web import static, server root = static.File("/var/www/htdocs") application = service.Application('web') site = server.Site(root) sc = service.IServiceCollection(application) i = internet.TCPServer(80, site) i.setServiceParent(sc)Site会封装(wrap)一个Resource对象--the root.
大部分高级配置可以通过root resource对象找出来(Most advanced configurations will be in the form of tweaking the root resource object.).
一般来说,root的children是基于文件系统的内容的。显式调用putChild方法也可以改写文件系统。
这里是两个例子。第一个添加一个/doc的child来服务安装包的文档,第二个为CGI脚本添加一个cgi-bin目录。
from twisted.internet import reactor from twisted.web import static, server root = static.File("/var/www/htdocs") root.putChild("doc", static.File("/usr/share/doc")) reactor.listenTCP(80, server.Site(root)) reactor.run()from twisted.internet import reactor from twisted.web import static, server, twcgi root = static.File("/var/www/htdocs") root.putChild("cgi-bin", twcgi.CGIDirectory("/var/www/cgi-bin")) reactor.listenTCP(80, server.Site(root)) reactor.run()
Modifying File Resources
Virtual HostsFile resources, root对象或者child对象,有两个经常需要修改的重要属性:indexNames和processors. indexNames决定哪些文件会被作为"index files"对待--当请求一个目录的时候会呈现出来。processors决定如何处理某些扩展名。
这里是一个针对两者的例子,创建一个site,这个site里面所有.rpy扩展都是资源脚本,并且查找index.rpy文件来相应对目录的请求。
from twisted.application import internet, service from twisted.web import static, server, script root = static.File("/var/www/htdocs") root.indexNames=['index.rpy'] root.processors = {'.rpy': script.ResourceScript} application = service.Application('web') sc = service.IServiceCollection(application) site = server.Site(root) i = internet.TCPServer(80, site) i.setServiceParent(sc)File对象还有一个叫做ignoreExt的方法。这个方法可以用来为用户提供无扩展名的URL,这样的话实现是可以隐藏的(意思是说URL请求时扩展名是可以隐藏的吧)。这里是一个例子:from twisted.application import internet, service from twisted.web import static, server, script root = static.File("/var/www/htdocs") root.ignoreExt(".rpy") root.processors = {'.rpy': script.ResourceScript} application = service.Application('web') sc = service.IServiceCollection(application) site = server.Site(root) i = internet.TCPServer(80, site) i.setServiceParent(sc)现在,一个/foo的URL可能会被名为foo.rpy的资源文件服务,如果没有名为foo的文件存在的话。
Virtual hosting是通过一个特别的资源(resource)实现的,这个资源应该作为root resource来使用--NameVirtualHost. NameVirtualHost有一个名为default的属性,这个属性持有(hold)默认的站点(website). 如果想要一个其他名字的不同的root,可以使用addHost方法。
from twisted.application import internet, service from twisted.web import static, server, vhost, script root = vhost.NameVirtualHost() # Add a default -- htdocs root.default=static.File("/var/www/htdocs") # Add a simple virtual host -- foo.com root.addHost("foo.com", static.File("/var/www/foo")) # Add a simple virtual host -- bar.com root.addHost("bar.com", static.File("/var/www/bar")) # The "baz" people want to use Resource Scripts in their web site baz = static.File("/var/www/baz") baz.processors = {'.rpy': script.ResourceScript} baz.ignoreExt('.rpy') root.addHost('baz', baz) application = service.Application('web') sc = service.IServiceCollection(application) site = server.Site(root) i = internet.TCPServer(80, site) i.setServiceParent(sc)
Advanced Techniques
因为配置是一个Python片段,那么就有可能用上Python的所有能量。下面是一些简单的例子:
# No need for configuration of virtual hosts -- just make sure # a directory /var/vhosts/<vhost name> exists: from twisted.web import vhost, static, server from twisted.application import internet, service root = vhost.NameVirtualHost() root.default = static.File("/var/www/htdocs") for dir in os.listdir("/var/vhosts"): root.addHost(dir, static.File(os.path.join("/var/vhosts", dir))) application = service.Application('web') sc = service.IServiceCollection(application) site = server.Site(root) i = internet.TCPServer(80, site) i.setServiceParent(sc) # Determine ports we listen on based on a file with numbers: from twisted.web import vhost, static, server from twisted.application import internet, service root = static.File("/var/www/htdocs") site = server.Site(root) application = service.Application('web') serviceCollection = service.IServiceCollection(application) for num in map(int, open("/etc/web/ports").read().split()): serviceCollection.addCollection(internet.TCPServer(num, site))
Running a Twisted Web Server
Serving Flat HTML在很多情况下,我们不会重复的使用twisted.web的常见使用模式。我们会使用Twisted的预先配置好的web server setup.
运行一个Twisted Web server最简单的方式是使用Twisted Daemonizer. 例如,下面这个命令会运行一个web server服务于一个特定目录下的静态文件:
% twistd web --path /path/to/web/content如果我们仅仅想要服务于我们自己的home目录下的内容,下面这样就可以了:% twistd web --path ~/public_html/我们可以在任何时间回到启动目录通过运行下述命令来停止server:
% kill `cat twistd.pid`还有其他一些配置可以使用:--port: 指定web server监控端口,默认是8080
--logfile: 指令日志文件
完整的选项可以用下面的方式查看:
% twistd web --help
Resource ScriptsTwisted Web Server服务于普通(flat)HTML文件时和它服务于其他任何普通(flat)文件一样。
资源脚本(a resource script)是一个以.rpy为后缀名的Python文件,它要求创建一个twisted.web.resource.Resource(的子类)的实例。
资源脚本有三个特别的变量:
__file__: .rpy文件的名字,包含完整的路径。这个变量是自动定义的并出现在名字空间(namespace)里。
registry: 类static.Registry的对象。可以一个class为key来访问和设置持久数据(persistent data)。
resource: 这个变量必须定义在脚本里,并设置成将要用来渲染页面的resource实例。
一个非常简单的资源脚本可能看上去是这样的:
from twisted.web import resource class MyGreatResource(resource.Resource): def render_GET(self, request): return "<html>foo</html>" resource = MyGreatResource()一个稍微复杂点的访问一些持久数据的资源脚本,可能看上去是这样的:from twisted.web import resource from SillyWeb import Counter counter = registry.getComponent(Counter) if not counter: registry.setComponent(Counter, Counter()) counter = registry.getComponent(Counter) class MyResource(resource.Resource): def render_GET(self, request): counter.increment() return "you are visitor %d" % counter.getValue() resource = MyResource()这里假定我们已经有SillyWeb.Counter模块了,这个模块实现如下所述的一些事情:class Counter: def __init__(self): self.value = 0 def increment(self): self.value += 1 def getValue(self): return self.value
Web UIs
Spreadable Web ServersNevom框架,作为Quotient工程的一部分,是一个为我们应用程序提供web界面的高级系统。Nevow使用Twisted Web但是它自身不是Twisted的一部分。
Twisted Web最有意思的应用之一就是分布式webserver; 用twisted.spread包做"spreadable"计算,多个server可以回复相同端口来的请求。在两个不同的目录下,运行下面这些命令:
% twistd web --user % twistd web --personal [other options, if you desire]一旦开始运行这些实例,打开http://localhost:8080/your_username.twistd/--我们会看到用--personal选项创建的页面。这里发生了什么呢?我们发出的请求通过一个PB连接被中心(用户)服务器(central server)转发给了我们自己的(私有)服务器(your own server).这项技术对于小型“社区”(community)站点非常有用;用这个demo的代码,我们可以把一个HTTP端口跟很多resources连接,这些resources在同一台机器上用不同权限运行着,或者在不同本地机器上运行着,或者甚至跨越网络在其他站点上。
Serving PHP/Perl/CGI默认,一个私人的(persional)server在拥有者的home目录下监听一个UNIX socket。--port选项可以使其监听一个其他的地址,比如在不同的位置的TCP/SSL server或者UNIX server。如果我们用这个选项使一个私有server监听不同的地址,中心服务器(central(User) server)会找不到它,但是如果用一个使用相同API定制的服务器作为中心服务器就可以找到。--port选项的另一个用途是使UNIX server变得强壮已应对系统崩溃。如果server崩溃了,UNIX socket遗留在文件系统中,私人(personal)server无法重启除非这些socket被移除。但是,如果在创建私人server的时提供--port unix:/home/username/.twistd-web-pb:wantPID=1选项,那么会有一个lockfile来跟踪server socket是否在使用,并在没被使用的时候自动删掉它。
所有跟CGI相关的东西都在twisted.web.twcgi这里,在这里我们可以找到我们需要继承的类,这些类是为了支持我们喜欢的语言的。如果我们使用的是non-unix操作系统(比如Windows)或者默认的resources给解析器的pathnames不正确,我们需要创建一种属于我们自己的resource。
下面的片段是一个服务于perl文件的.rpy文件。twisted.web.twcgi有更多跟twisted.web和CGI相关的的例子。
from twisted.web import static, twcgi class PerlScript(twcgi.FilteredScript): filter = '/usr/bin/perl' # Points to the perl parser resource = static.File("/perlsite") # Points to the perl website resource.processors = {".pl": PerlScript} # Files that end with .pl will be # processed b PerlScript resource.indexNames = ['index.pl']
Serving WSGI Applications
Using VHostMonsterWSGI是Web Server Gateway Interface. 它是用于web servers和application servers跟Python web applications通信的一项规范。所有现代Python web frameworks支持WSGI接口。
启动WSGI应用程序最简单的方式是使用twistd命令:
% twistd -n web --wsgi=helloworld.application这里假定我们有一个名为application的WSGI应用在我们helloworld模块/包里,这个应用看上去可能是这样的:
def application(environ, start_response): """Basic WSGI Application""" start_response('200 OK', [('Content-type','text/plain')]) return ['Hello World!']上述部分(setup)适合于很多application,这种应用是在站点的根目录下服务WSGI应用程序。然后,为了更好的控制,Twisted支持把WSGI applications作为resources(twisted.web.wsgi.WSGIResource)使用。这里是一个作为站点的root resuorce而提供服务的WSGI application,在下面的tac文件中:
from twisted.web import server from twisted.web.wsgi import WSGIResource from twisted.python.threadpool import ThreadPool from twisted.internet import reactor from twisted.application import service, strports # Create and start a thread pool, wsgiThreadPool = ThreadPool() wsgiThreadPool.start() # ensuring that it will be stopped when the reactor shuts down reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop) def application(environ, start_response): """A basic WSGI application""" start_response('200 OK', [('Content-type','text/plain')]) return ['Hello World!'] # Create the WSGI resource wsgiAppAsResource = WSGIResource(reactor, wsgiThreadPool, application) # Hooks for twistd application = service.Application('Twisted.web.wsgi Hello World Example') server = strports.service('tcp:8080', server.Site(wsgiAppAsResource)) server.setServiceParent(application)它可以像其他.tac文件一样运行:% twistd -ny myapp.tac因为WSGI的同步特性,每个应用程序调用(每次请求)是在一个线程里完成的,结果被写回给web server. 为此,需要使用twisted.python.threadpool.ThreadPool实例。
一种常见的使用是这样的,一个server(比如Apache)用多个名字反向代理(Apache的话是使用mod_proxy)不同的(可能在不同的机器上)内部web server。简单(naive)配置容易导致误解:内部server坚信自己使用"internal-name:port"的方式在运行,并且生成这种效果的URL,而被客户端收到以后就发现完全错了。
Apache有ProxyPassReverse指令(directive),但是它不够好(it is really a hack and is nowhere near comprehensive enough)。作为替代,在内部web server是Twisted Web的情况下推荐使用VHostMonster.
对Twisted来说,使用VHostMonster很简单:只是产生一个名为vhost.rpy文件并包含下述代码:
from twisted.web import vhost resource = vhost.VHostMonsterResource()确保web server为rpy扩展配置了正确的processors("twistd web --path" 产生的web server默认是这么配置的).对Apache来说,不要使用下面ProxyPass指令:
<VirtualHost ip-addr> ProxyPass / http://localhost:8538/ ServerName example.com </VirtualHost>而是使用下面这些指令:<VirtualHost ip-addr> ProxyPass / http://localhost:8538/vhost.rpy/http/example.com:80/ ServerName example.com </VirtualHost>这里是一个使用Twisted Web的反向代理:from twisted.application import internet, service from twisted.web import proxy, server, vhost vhostName = 'example.com' reverseProxy = proxy.ReverseProxyResource('internal', 8538, '/vhost.rpy/http/'+vhostName+'/') root = vhost.NameVirtualHost() root.addHost(vhostName, reverseProxy) site = server.Site(root) application = service.Application('web-proxy') sc = service.IServiceCollection(application) i = internet.TCPServer(80, site) i.setServiceParent(sc)
Rewriting URLs
Knowing When We're Not Wanted有时候在传递Request对象之前修改它的内容是很方便(convenient)的。因为改写URL是很常见的,twisted.web.rewrite模块跟Apache的mod_rewrite类似。这个模块通过封装(wrapping)twisted.web.rewrite.RewriterResource得到的resource提供rewrite规则来完成需求。改写规则是接受request对象的函数,同时可能会修改request对象。在所有改写规则运行后,url处理链路上的子处理过程会继续进行,并好像那个封装后的resource(注意,不是RewriterResource)就是下一个child过程一样(擦,这里不太明白,反正就是这么个意思)。
这里是一个例子,只使用了Twisted提供的一个的规则:
default_root = rewrite.RewriterResource(default, rewrite.tildeToUsers)这会导致URL "/~foo/bar.html"被当成"/users/foo/bar.html". 如果把default的users child设置为distrib.UserDirectory,那么就给出了一个类似于"自第一个NCSA服务器以来web server的经典配置"的配置。
As-Is Serving有时,知道对端何时断开了链接是很有用的。这里是一个这样的例子:
from twisted.web.resource import Resource from twisted.web import server from twisted.internet import reactor from twisted.python.util import println class ExampleResource(Resource): def render_GET(self, request): request.write("hello world") d = request.notifyFinish() d.addCallback(lambda _: println("finished normally")) d.addErrback(println, "error") reactor.callLater(10, request.finish) return server.NOT_DONE_YET resource = ExampleResource()它允许我们对日志文件做统计来看有多少用户在仅仅十秒钟之后就断开连接(are frustrated,受挫??)了。
有时,我们想直接发送头部和状态码。当然我们可以用ResourceScript来做这个,一个更简单的方式是用ASISProcessor. 要使用它,举个例子,需要把它作为一个processor添加进来处理.asis后缀的文件。这里是一个样本文件:
HTTP/1.0 200 OK Content-Type: text/html Hello world