Welcome, guest | Sign In | My Account | Store | Cart
from urlparse import urlparse, urlsplit
from cgi import parse_qs
import cStringIO as StringIO

from twisted.internet import defer
from twisted.web import server, resource, client



def encodeFormData(arg, value):
    """Encode data as a multipart/form-data
    """
 
    BOUNDARY = '----------BOUNDARY'
   
    l = []
    l.append('--' + BOUNDARY)
    l.append('Content-Disposition: form-data; name="%s"' % arg)
    l.append('')
    l.append(value)
    l.append('--' + BOUNDARY + '--')
    l.append('')

    body = '\r\n'.join(l)
    contentType = 'multipart/form-data; boundary=%s' % BOUNDARY

    return body, contentType


class FakeTransport(object):
    def __init__(self):
        self.io = StringIO.StringIO()

    
    def writeSequence(self, sequence):
        self.io.writelines(sequence)

    def write(self, data):
        self.io.write(data)

    def getData(self):
        return self.io.getvalue()


class MarkupValidator(resource.Resource):
    """A simple gateway to a validator service for Twisted Web.
    """

    # Please, install a validator on your server!
    uri = 'http://validator.w3.org/check'
    arg = 'fragment'

    def __init__(self, site):
        """site is the site object of your twisted.web server
        """

        self.site = site


    def render_GET(self, request):
        def finish(data):
            # Write the data back to our client
            request.clientproto = clientproto
            transport.write(data)
            transport.loseConnection()


        # Get the referer and parse it
        referer = request.getHeader('referer')
        scheme, netloc, path, parameters, query, fragment = urlparse(referer)
        args = parse_qs(query)
        
        transport = request.transport
        clientproto = request.clientproto

        # Modify the original request
        request.uri = referer
        request.path = path
        request.args = args
        request.clientproto = 'HTTP/1.0'  # we don't want chunk encoding
        request.transport = FakeTransport()

        # Reload the modified request on the server, without using HTTP
        deferred = request.notifyFinish()
        request.process()

        # XXX TODO handle errors
        deferred.addCallback(lambda _: request.transport.getData()
                             ).addCallback(self.validate, request
                                           ).addCallback(finish)
        
        return server.NOT_DONE_YET

    def validate(self, data, request):
        # We need only the body
        data = data[data.find('\r\n\r\n') + 4:]

        # Build the request for the validator service, using the
        # original request as the base
        headers = request.received_headers
        data, contentType = encodeFormData(self.arg, data)
        
        headers['content-type'] = contentType
        headers.pop('cookie', None)
        headers.pop('referer', None)
        headers.pop('host', None)
        
        return client.getPage(
            self.uri, method='POST', headers=headers, postdata=data 
            )


if __name__ == '__main__':
    """A simple usage example.
    """

    from twisted.web import server
    from twisted.internet import reactor


    class Simple(resource.Resource):
        def render_GET(self, request):
            return """<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" 
      lang="en" xml:lang="en">
  <head>
    <title>twvalidator example</title>
  </head>
  <body>
    <h1>twvalidator example</h1>

    <p><a href="validate">validate</a></p>
  </body>
</html>"""

        def getChild(self, name, request):
            if name == '':
                return self
            return resource.Resource.getChild(
                self, name, request)


    root = Simple()
    site = server.Site(root)

    root.putChild('validate', MarkupValidator(site))

    reactor.listenTCP(8080, site)
    reactor.run()

History