zoukankan      html  css  js  c++  java
  • Twisted网络编程必备(5)

    转自:http://www.yybug.com/read-htm-tid-15324.html

    4.4 管理资源等级

    WEB应用中的路径通常使用分级目录管理。例如如下URL:

    http://example.com/people

    http://example.com/people/charles

    http://example.com/people/charles/contact

    这里可以很清楚的看出等级划分。页面/people/charles是/people的子页面,而页面/people/charles/contact是/people/charles的子页面。等级中的每个页面都有特定的意义:/people/charles是一个人,而/people/charles/contact是一项数据,charles的数据。

    WEB服务器的缺省行为是将PATH等级映射到磁盘上的文件。客户端的每次请求都是对应特定的资源,服务器查找文件并定位磁盘路径,然后将文件内容或者可执行文件的执行结果作为响应。在WEB应用中可以人为的生成一个对应路径文件的内容。例如,你的数据并没有存储在磁盘上,而是在一个关系数据库或者另一个服务器上。或者你想要在请求时自动创建资源。类似这种情况,最好的办法是为等级浏览创建自己的逻辑。

    编写自己的资源管理逻辑可以帮助你管理安全。而不是打开WEB服务器的整个目录,你可以有选择的控制哪些文件是可以访问的。

    4.4.1 下面如何做?

    twisted.web.resource和twisted.web.static还有twisted.web.server模块提供了比twisted.web.http.Resource更高层次的请求管理类,你可以使用这些来设置一个WEB服务器来处理多种逻辑等级的资源。例子4-4使用了这些类来建立十六进制颜色代码。请求资源/color/hex,hex是十六进制的颜色代码,你可以得到一个背景为#hex的页面。对应每一种可能出现的颜色可能,服务器动态创建资源。

    代码
    from twisted.web import resource,static,server
    class ColorPage(resource.Resource):
    def __init__(self,color):
    self.color
    =color
    def render(self,request):
    return """
    <html>
    <head>
    <title>Color: %s</title>
    <link type='text/css' href='/styles.css' rel='Stylesheet' />
    </head>
    <body style='background-color: #%s'>
    <h1>This is #%s.</h1>
    <p style='background-color: white'>
    <a href='/color/'>Back</a>
    </p>
    </body>
    </html>
    """ % (self.color, self.color, self.color)

    class ColorRoot(resource.Resource):
    def __init__(self):
    resource.Resource.
    __init__(self)
    self.requestedColors
    =[]
    self.putChild(
    '',ColorIndexPage(self.requestColors))
    def render(self,request):
    # redirect /color -> /color/
    request.redirect(request.path+'/')
    return 'please use /colors/ instead.'
    def getChild(self,path,request):
    if path not in self.requestedColors:
    self.requestedColors.append(path)
    return ColorPage(path)
    class ColorIndexPage(resource.Resource):
    def __init__(self,requestColorsList):
    resource.Resource.
    __init__(self)
    self.requestedColors
    =requestedColorsList
    def render(self,request):
    request.write(
    """
    <html>
    <head>
    <title>Colors</title>
    <link type='text/css' href='/styles.css' rel='Stylesheet' />
    </head>
    <body>
    <h1>Colors</h1>
    To see a color, enter a url like
    <a href='/color/ff0000'>/color/ff0000</a>. <br />
    Colors viewed so far:
    <ul>
    """)
    for color in self.requestedColors:
    request.write(
    "<li><a href="/blog/%s" style='color: #%s'>%s</a></li>" % (
    color, color, color))
    request.write(
    """
    </ul>
    </body>
    </html>
    """)
    return ""
    class HomePage(resource.Resource):
    def render(self,request):
    return """
    <html>
    <head>
    <title>Colors</title>
    <link type='text/css' href='/styles.css' rel='Stylesheet' />
    </head>
    <body>
    <h1>Colors Demo</h1>
    What's here:
    <ul>
    <li><a href='/color'>Color viewer</a></li>
    </ul>
    </body>
    </html>
    """

    if __name__=='__main__':
    from twisted.internet import reactor
    root
    =resource.Resource()
    root.putChild(
    '',HomePage())
    root.putChild(
    'color',ColorRoot())
    root.putChild(
    'styles.css',static.File('styles.css'))
    site
    =server.Site(root)
    reactor.listenTCP(
    8000,site)
    reactor.run()

    例子4-4引用了静态文件。所以需要在resourcetree.py脚本目录下创建一个styles.css文件。内容如下:

    代码
    body {
    font-family
    : Georgia, Times, serif;
    font-size
    : 11pt;
    }
    h1
    {
    margin
    : 10px 0;
    padding
    : 5px;
    background-color
    : black;
    color
    : white;
    }
    a
    {
    font-family
    : monospace;
    }
    p
    {
    padding
    : 10px;
    }

    运行resourcetree.py脚本,将会在8000端口启动一个WEB服务器。下面是服务器全部可用路径:

    /        主页

    /css    虚拟的CSS资源

    /css/styles.css    静态文件styles.css

    /colors/ 颜色查看页面

    /colors/hexcolor 按照背景色为#hexcolor的页面

    尝试通过http://localhost:8000/colors/00abef来访问,将会得到背景色为#00abef的页面,大约是亮蓝色。

    可以随便试试其他颜色。同样可以进入http://localhost:8000/,选择可选答案。

    4.4.2 它们如何工作?

    例子4.4从twisted.web包中引入了几个类:resource.Resource、static.File、server.Site。每个resource.Resource对象做两件事。首先,定义请求的资源如何处理。第二,定义请求子资源的Resource对象。

    例如查看类ColorRoot。在这个类的实例稍后被加入了/colors这个等级资源。初始化时,ColorRoot使用putChild方法插入ColorIndexPage这个资源座位''资源。这意味着所有对/colors/的请求都由ColorIndexPage对象来处理。

    你可以把他们想象为等价的,但是/stuff和/stuff/是不同的。浏览器在解释相对路径时,对是否加上斜线的处理方法是不同的。在第一个例子中,对"otherpage"的请求会解释为"http://example.com/otherpage",在第二个例子中解释为"http://example.com/stuff/otherpage"。

    如果你不清楚(explicit)服务器代码,这个问题可能会再次郁闷你。最好是预先设计好是否需要在URI末尾加上斜线,并重定向请求。Resource类将会简化这些操作。如果设置了addSlash属性为True,一个Resource会自动在找不到对应资源时自动在URL末尾添加斜线来再次查找。

    4.4 管理资源等级

    4.6 运行HTTP代理服务器

    除了HTTP服务器和客户端以外,twisted.web还包含了HTTP代理服务器的支持。一个代理服务器是一个服务器和一个客户端。他接受来自客户端的请求(作为服务器)并将他们转发到服务器(作为客户端)。然后将响应发送回客户端。HTTP代理服务器可以提供很多有用的服务:缓存、过滤和使用情况报告。下面的例子展示了如何使用Twisted构建一个HTTP代理服务器。

    4.6.1 下面如何做?

    twisted.web包包含了twisted.web.proxy,这个模块包含了HTTP代理服务器。例子4-7构建了一个简单的代理服务器。

    代码
    from twisted.web import proxy,http
    from twisted.internet import reactor
    from twisted.python import log
    import sys
    log.startLogging(sys.stdout)
    class ProxyFactory(http.HTTPFactory):
    protocol
    =proxy.Proxy
    reactor.listenTCP(
    8001,ProxyFactory())
    reactor.run()

    运行simpleproxy.py脚本将会在8001端口启动代理服务器。在浏览器中设置这个代理服务器可以作为代理进行测试。对log.startLogging的调用将会把HTTP日志信息记录在stdout中,并可以直接查看。

    $ python simpleproxy.py

    2005/06/13 00:22 EDT [-] Log opened.

    2005/06/13 00:22 EDT [-] __main__.ProxyFactory starting on 8001

    ... ...

    这虽然给出了一个代理服务器,但是实际上没什么用处。例子4-8提供了更多的功能,可以跟踪最常使用的网页。

    代码
    import sgmllib.re
    from twisted.web import proxy,http
    import sys
    from twisted.python import log
    log.startLogging(sys.stdout)
    WEB_PORT
    =8000
    PROXY_PORT
    =8001
    class WordParser(sgmllib.SGMLParser):
    def __init__(self):
    sgmllib.SGMLParser.
    __init__(self)
    self.chardata
    =[]
    self.inBody
    =False
    def start_body(self,attrs):
    self.inBody
    =True
    def end_body(self):
    self.inBody
    =False
    def handle_data(self,data):
    if self.inBody:
    self.chardata.append(data)
    def getWords(self):
    #解出单词
    wordFinder=re.compile(r'\w*')
    words
    =wordFinder.findall("".join(self.chardata))
    words
    =filter(lambda word: word.strip(), words)
    print "WORDS ARE", words
    return words
    class WordCounter(object):
    ignoredWords
    ="the a of in from to this that and or but is was be can could i you they we at".split()
    def __init__(self):
    self.words
    =()
    def addWords(self,words):
    for word in words:
    word
    =word.lower()
    if not word in self.ignoredWords:
    currentCount
    =self.words.get(word,0)
    self.words[word]
    =currentCount+1
    class WordCountProxyClient(proxy.ProxyClient):
    def handleHeader(self,key,value):
    proxy.ProxyClient.handleHeader(self,key,value)
    if key.lower()=="content-type":
    if value.split(';')[0]=='text/html':
    self.parser
    =WordParser()
    def handleResponsePart(self,data):
    proxy.ProxyClient.handleResponsePart(self,data)
    if hasattr(self,'parser'):
    self.parser.feed(data)
    def handleResponseEnd(self):
    proxy.ProxyClient.handleResponseEnd(self)
    if hasattr(self,'parser'):
    self.parser.close()
    self.father.wordCounter.addWords(self.parser.getWords())
    del(self.parser)
    class WordCountProxyClientFactory(proxy.ProxyClientFactory):
    def buildProtocol(self,addr):
    client
    =proxy.ProxyClientFactory.buildProtocol(self,addr)
    #升级proxy.proxyClient对象到WordCountProxyClient
    client.__class__=WordCountProxyClient
    return client
    class WordCountProxyRequest(proxy.ProxyRequest):
    protocols
    ={'http':WordCountProxyClientFactory)
    def __init__(self,wordCounter,*args):
    self.wordCounter
    =wordCounter
    proxy.ProxyRequest.
    __init__(self,*args)
    class WordCountProxy(proxy.Proxy):
    def __init__(self,wordCounter):
    self.wordCounter
    =wordCounter
    proxy.Proxy.
    __init__(self)
    def requestFactory(self,*args)
    return WordCountProxyRequest(self.wordCounter,*args)
    class WordCountProxyFactory(http.HTTPFactory):
    def __init__(self,wordCount):
    self.wordCounter
    =wordCounter
    http.HTTPFactory.
    __init__(self)
    def buildProtocol(self,addr):
    protocol
    =WordCountProxy(self.wordCounter)
    return protocol
    #使用WEB接口展示记录的接口
    class WebReportRequest(http.Request):
    def __init__(self,wordCounter,*args):
    self.wordCounter
    =wordCounter
    http.Request.
    __init__(self,*args)
    def process(self):
    self.setHeader(
    "Content-Type",'text/html')
    words
    =self.wordCounter.words.items()
    words.sort(
    lambda(w1,c1),(w2,c2): cmp(c2,c1))
    for word,count in words:
    self.write(
    "<li>%s %s</li>"%(word,count))
    self.finish()
    class WebReportChannel(http.HTTPChannel):
    def __init__(self,wordCounter):
    self.wordCounter
    =wordCounter
    http.HTTPChannel.
    __init__(self)
    def requestFactory(self,*args):
    return WebReportRequest(self.wordCounter,*args)
    class WebReportFactory(http.HTTPFactory):
    def __init__(self,wordCounter):
    self.wordCounter
    =wordCounter
    http.HTTPFactory.
    __init__(self)
    def buildProtocol(self,addr):
    return WebReportChannel(self.wordCounter)
    if __name__=='__main__':
    from twisted.internet import reactor
    counter
    =WordCounter()
    prox
    =WordCountProxyFactory(counter)
    reactor.listenTCP(PROXY_PORT,prox)
    reactor.listenTCP(WEB_PORT,WebReportFactory(counter))
    reactor.run()

    运行wordcountproxy.py将浏览器的代理服务器设置到8001端口。浏览其他站点,或者访问http://localhost:8000/,将可以看到访问过的站点的单词频率。

    4.6.2 它是如何工作的?

    在例子4-8中有很多个类,但大多数是连接用的。只有很少的几个做实际工作。最开始的两个类WordParser和WordCounter,用于从HTML文档中解出单词符号并计算频率。第三个类WordCountProxy客户端包含了查找HTML文档并调用WordParser的任务。

    因为代理服务器同时作为客户端和服务器,所以需要使用大量的类。有一个ProxyClientFactory和ProxyClient,提供了Factory/Protocol对来支持客户端连接。为了响应客户端的连接,需要使用ProxyRequest,它是HTTPFactory的子类,还有Proxy,是http.HTTPChannel的子类。它们对建立一个普通的HTTP服务器是有必要的:HTTPFactory使用Proxy作它的协议(Protocol),而代理Proxy HTTPChannel使用ProxyRequest作为它的RequestFactory。如下是客户端发送请求的事件处理流程:

    ·客户端建立到代理服务器的连接。这个连接被HTTPFactory所处理。

    ·HTTPFactory.buildProtocol会创建一个Proxy对象用来对客户端发送和接收数据。

    ·当客户端发送请求时,Proxy创建ProxyRequest来处理。

    ·ProxyRequest查找客户端请求的服务器。并创建ProxyClientFactory并调用reactor.connectTCP来通过factory连接服务器。

    ·一旦ProxyClientFactory连接到服务器,就会创建ProxyClient这个Protocol对象来发送和接收数据。

    ·ProxyClient发送原始请求。作为响应,它将客户端发来的请求发送给服务器。这是通过调用self.father.transport.write实现的。self.father是一个Proxy对象,正在管理着客户端连接对象。

    这是一大串类,但实际上是分工明确的将工作由一个类传递到另一个类。但这是很重要的。为代理模块的每一个类提供一个子类,你可以完成每一步的控制。

    整个例子4-8中最重要的一个技巧。ProxyClientFactory类有一个buildProtocol方法,可以包装好,供ProxyClient作为Protocol。它并不提供任何简单的方法来提供你自己的ProxyClient子类。解决办法是使用Python的__class__属性来替换升级ProxyClient对象来放回ProxyClientFactory.buildProtocol,这些将ProxyClient改变为WordCountProxyClient。

    作为代理服务器的附加功能。例子4-8提供了标准的WEB服务器,在8000端口,可以显示从代理服务器来的当前单词统计数量。由此可见,在你的应用中包含一个内嵌的HTTP服务器是多么的方便,这种方式可以很好的提供给远程来显示相关状态信息。

  • 相关阅读:
    smart client优势在那里? (草稿)
    MSN to expand free email storage to 250MB
    转移阵地了,新地址:http://spaces.msn.com/members/PuGong
    关于XMLHTTP object的OPEN方法
    创建第一个Windows Phone应用程序(一)
    Windows 平台下安装Cygwin后,sshd服务无法启动
    ASP.NET MVC3 Use Remote Validation
    ASP.NET MVC3中使用AllowHtml attribute
    HTML5 Canvas实现简单的俄罗斯方块
    创建第一个Windows Phone应用程序(二)
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/1884550.html
Copyright © 2011-2022 走看看