durusmail: quixote-users: An SCGI client module for Twisted Web
An SCGI client module for Twisted Web
2004-07-31
2004-07-31
2004-07-31
2004-07-31
An SCGI client module for Twisted Web
Jon Dyte
2004-07-31
Hi Matt

I'd certainly be interested in seeing that script, as I'd like to
compare it against the one below, which was my solution to the same
problem. However I dont really know enough about twisted to be sure this
was the right way to do it.

To be honest i cobbled this together after looking at twisted_http.py
and scanning some of the twisted src files. twisted/web/twcgi may well
have been a better place to start had I known it existed!

Cheers
Jon

from twisted.protocols import http
from twisted.web import proxy,server
from twisted.internet import reactor, protocol

HTTP_PORT = 8098
SCGI_PORT = 4000
SCGI_PROTOCOL_VERSION = "1"

class SCGIProtocol(protocol.Protocol):

    def __init__(self,request):
        self.request = request
        self.data = ""

    def connectionMade(self):
        self.transport.write(self.request.get_scgi_netstring())

    def connectionLost(self, reason):
        self.request.write(self.data.replace("Status:","HTTP/1.1"))
        self.request.finish()

    def dataReceived(self,data):
        self.data += data


class SCGIReverseProxyRequest(proxy.ReverseProxyRequest):

    def process(self):
        scgi_ns=self.get_scgi_netstring()
        cc = protocol.ClientCreator(reactor,SCGIProtocol,self)
        df = cc.connectTCP("localhost", SCGI_PORT)
        self.startedWriting = 1

    def get_scgi_netstring (self):
        host = self.getHeader('host') or self.getHost()
        serverName = host.split(':')[0]
        content_type = self.getHeader('content-type')
        typ, remote_addr, remote_port = self.transport.getPeer()
        t = range(22)
        bdy = self.content.read()
        t[0] = "CONTENT_LENGTH",str(len(bdy or ""))
        t[1]= "SCGI", SCGI_PROTOCOL_VERSION
        t[2]= "SERVER_SOFTWARE", server.version or ""
        t[3]= "SERVER_PROTOCOL", self.clientproto or ""
        t[4]= "SERVER_NAME", serverName or ""
        t[5]= "SERVER_ADMIN", ""
        t[6]= "SERVER_ADDR", ""
        t[7]= "SERVER_PORT", str(HTTP_PORT)
        t[8]= "REMOTE_ADDR", self.getClientIP() or ""
        t[9]= "REMOTE_PORT", str(remote_port) or ""
        t[10]= "REMOTE_USER", self.getUser() or ""
        t[11]= "REQUEST_METHOD", self.method or ""
        t[12]= "REQUEST_URI", self.uri or ""
        x = self.uri.split('?', 2)
        if len(x) == 2:
            t[13]= "QUERY_STRING", x[1] or ""
        else:
            t[13]= "QUERY_STRING",""
        t[14]= "SCRIPT_NAME", self.uri
        t[15]= "HTTP_USER_AGENT", self.getHeader("User-Agent") or ""
        t[16]= "HTTP_COOKIE", self.getHeader("Cookie") or ""
        t[17]= "HTTPS", "" #lookup_env(r, "HTTPS")
        t[18]= "CONTENT_TYPE", self.getHeader("Content-type") or ""
        t[19]= "DOCUMENT_ROOT", "" #ap_document_root(r)
        t[20]= "HTTP_ACCEPT", self.getHeader("Accept") or ""
        t[21]= "HTTP_REFERER", self.getHeader("Referer") or ""
        ### now make a netstring
        buffer="".join([ "%s\0%s\0" % i for i in t])
        ns = "%lu:%s,%s" % (len(buffer),buffer,bdy)
        return ns

class SCGIReverseProxy(proxy.ReverseProxy):
    requestFactory = SCGIReverseProxyRequest

def main():
    f = http.HTTPFactory()
    f.protocol = SCGIReverseProxy
    reactor.listenTCP(HTTP_PORT, f)
    reactor.run()


if __name__ == '__main__':
    main()


reply