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('--' + BOUNDARY + '--')

    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):

    def write(self, 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

        # 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()

        # XXX TODO handle errors
        deferred.addCallback(lambda _: request.transport.getData()
                             ).addCallback(self.validate, request
        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"

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

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

        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)