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服务器是多么的方便,这种方式可以很好的提供给远程来显示相关状态信息。

  • 相关阅读:
    苹果一体机发射Wi-Fi
    iphone 屏蔽系统自动更新,消除设置上的小红点
    data parameter is nil 异常处理
    copy与mutableCopy的区别总结
    java axis2 webservice
    mysql 远程 ip访问
    mysql 存储过程小问题
    mysql游标错误
    is not writable or has an invalid setter method错误的解决
    Struts2中关于"There is no Action mapped for namespace / and action name"的总结
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/1884550.html
Copyright © 2011-2022 走看看