| 1 |
import warnings |
|---|
| 2 |
import os |
|---|
| 3 |
import urllib |
|---|
| 4 |
from zope.interface import implements |
|---|
| 5 |
|
|---|
| 6 |
from twisted.internet import protocol, address |
|---|
| 7 |
from twisted.internet import reactor, interfaces |
|---|
| 8 |
from twisted.web2 import http, http_headers, server, responsecode |
|---|
| 9 |
|
|---|
| 10 |
class BaseCGIChannelRequest(protocol.Protocol): |
|---|
| 11 |
implements(interfaces.IHalfCloseableProtocol) |
|---|
| 12 |
|
|---|
| 13 |
finished = False |
|---|
| 14 |
requestFactory = http.Request |
|---|
| 15 |
request = None |
|---|
| 16 |
|
|---|
| 17 |
def makeRequest(self, vars): |
|---|
| 18 |
headers = http_headers.Headers() |
|---|
| 19 |
http_vers = http.parseVersion(vars['SERVER_PROTOCOL']) |
|---|
| 20 |
if http_vers[0] != 'http' or http_vers[1] > 1: |
|---|
| 21 |
_abortWithError(responsecode.INTERNAL_SERVER_ERROR, "Twisted.web CGITransport: Unknown HTTP version: " % vars['SERVER_PROTOCOL']) |
|---|
| 22 |
|
|---|
| 23 |
secure = vars.get("HTTPS") in ("1", "on") |
|---|
| 24 |
port = vars.get('SERVER_PORT') or 80 |
|---|
| 25 |
server_host = vars.get('SERVER_NAME') or vars.get('SERVER_ADDR') or 'localhost' |
|---|
| 26 |
|
|---|
| 27 |
self.hostinfo = address.IPv4Address('TCP', server_host, port), bool(secure) |
|---|
| 28 |
self.remoteinfo = address.IPv4Address( |
|---|
| 29 |
'TCP', vars.get('REMOTE_ADDR', ''), vars.get('REMOTE_PORT', 0)) |
|---|
| 30 |
|
|---|
| 31 |
uri = vars.get('REQUEST_URI') |
|---|
| 32 |
if not uri: |
|---|
| 33 |
qstr = vars.get('QUERY_STRING', '') |
|---|
| 34 |
if qstr: |
|---|
| 35 |
qstr = "?"+urllib.quote(qstr, safe="") |
|---|
| 36 |
uri = urllib.quote(vars['SCRIPT_NAME'])+urllib.quote(vars.get('PATH_INFO', ''))+qstr |
|---|
| 37 |
|
|---|
| 38 |
for name,val in vars.iteritems(): |
|---|
| 39 |
if name.startswith('HTTP_'): |
|---|
| 40 |
name = name[5:].replace('_', '-') |
|---|
| 41 |
elif name == 'CONTENT_TYPE': |
|---|
| 42 |
name = 'content-type' |
|---|
| 43 |
else: |
|---|
| 44 |
continue |
|---|
| 45 |
headers.setRawHeaders(name, (val,)) |
|---|
| 46 |
|
|---|
| 47 |
self._dataRemaining = int(vars.get('CONTENT_LENGTH', '0')) |
|---|
| 48 |
self.request = self.requestFactory(self, vars['REQUEST_METHOD'], uri, http_vers[1:3], self._dataRemaining, headers, prepathuri=vars['SCRIPT_NAME']) |
|---|
| 49 |
|
|---|
| 50 |
def writeIntermediateResponse(self, code, headers=None): |
|---|
| 51 |
"""Ignore, CGI doesn't support.""" |
|---|
| 52 |
pass |
|---|
| 53 |
|
|---|
| 54 |
def write(self, data): |
|---|
| 55 |
self.transport.write(data) |
|---|
| 56 |
|
|---|
| 57 |
def finish(self): |
|---|
| 58 |
if self.finished: |
|---|
| 59 |
warnings.warn("Warning! request.finish called twice.", stacklevel=2) |
|---|
| 60 |
return |
|---|
| 61 |
self.finished = True |
|---|
| 62 |
self.transport.loseConnection() |
|---|
| 63 |
|
|---|
| 64 |
def getHostInfo(self): |
|---|
| 65 |
return self.hostinfo |
|---|
| 66 |
|
|---|
| 67 |
def getRemoteHost(self): |
|---|
| 68 |
return self.remoteinfo |
|---|
| 69 |
|
|---|
| 70 |
def abortConnection(self, closeWrite=True): |
|---|
| 71 |
self.transport.loseConnection() |
|---|
| 72 |
|
|---|
| 73 |
def registerProducer(self, producer, streaming): |
|---|
| 74 |
self.transport.registerProducer(producer, streaming) |
|---|
| 75 |
|
|---|
| 76 |
def unregisterProducer(self): |
|---|
| 77 |
self.transport.unregisterProducer() |
|---|
| 78 |
|
|---|
| 79 |
def writeConnectionLost(self): |
|---|
| 80 |
self.loseConnection() |
|---|
| 81 |
|
|---|
| 82 |
def readConnectionLost(self): |
|---|
| 83 |
if self._dataRemaining > 0: |
|---|
| 84 |
|
|---|
| 85 |
self.loseConnection() |
|---|
| 86 |
|
|---|
| 87 |
class CGIChannelRequest(BaseCGIChannelRequest): |
|---|
| 88 |
cgi_vers = (1, 0) |
|---|
| 89 |
|
|---|
| 90 |
def __init__(self, requestFactory, vars): |
|---|
| 91 |
self.requestFactory=requestFactory |
|---|
| 92 |
cgi_vers = http.parseVersion(vars['GATEWAY_INTERFACE']) |
|---|
| 93 |
if cgi_vers[0] != 'cgi' or cgi_vers[1] != 1: |
|---|
| 94 |
_abortWithError(responsecode.INTERNAL_SERVER_ERROR, "Twisted.web CGITransport: Unknown CGI version %s" % vars['GATEWAY_INTERFACE']) |
|---|
| 95 |
self.makeRequest(vars) |
|---|
| 96 |
|
|---|
| 97 |
def writeHeaders(self, code, headers): |
|---|
| 98 |
l = [] |
|---|
| 99 |
code_message = responsecode.RESPONSES.get(code, "Unknown Status") |
|---|
| 100 |
|
|---|
| 101 |
l.append("Status: %s %s\n" % (code, code_message)) |
|---|
| 102 |
if headers is not None: |
|---|
| 103 |
for name, valuelist in headers.getAllRawHeaders(): |
|---|
| 104 |
for value in valuelist: |
|---|
| 105 |
l.append("%s: %s\n" % (name, value)) |
|---|
| 106 |
l.append('\n') |
|---|
| 107 |
self.transport.writeSequence(l) |
|---|
| 108 |
|
|---|
| 109 |
def dataReceived(self, data): |
|---|
| 110 |
if self._dataRemaining <= 0: |
|---|
| 111 |
return |
|---|
| 112 |
|
|---|
| 113 |
if self._dataRemaining < len(data): |
|---|
| 114 |
data = data[:self._dataRemaining] |
|---|
| 115 |
self._dataRemaining -= len(data) |
|---|
| 116 |
self.request.handleContentChunk(data) |
|---|
| 117 |
if self._dataRemaining == 0: |
|---|
| 118 |
self.request.handleContentComplete() |
|---|
| 119 |
|
|---|
| 120 |
def connectionMade(self): |
|---|
| 121 |
self.request.process() |
|---|
| 122 |
if self._dataRemaining == 0: |
|---|
| 123 |
self.request.handleContentComplete() |
|---|
| 124 |
|
|---|
| 125 |
def connectionLost(self, reason): |
|---|
| 126 |
if reactor.running: |
|---|
| 127 |
reactor.stop() |
|---|
| 128 |
|
|---|
| 129 |
def startCGI(site): |
|---|
| 130 |
"""Call this as the last thing in your CGI python script in order to |
|---|
| 131 |
hook up your site object with the incoming request. |
|---|
| 132 |
|
|---|
| 133 |
E.g.: |
|---|
| 134 |
>>> from twisted.web2 import channel, server |
|---|
| 135 |
>>> if __name__ == '__main__': |
|---|
| 136 |
... channel.startCGI(server.Site(myToplevelResource)) |
|---|
| 137 |
|
|---|
| 138 |
""" |
|---|
| 139 |
from twisted.internet.stdio import StandardIO |
|---|
| 140 |
StandardIO(CGIChannelRequest(site, os.environ)) |
|---|
| 141 |
reactor.run() |
|---|
| 142 |
|
|---|
| 143 |
__all__ = ['startCGI'] |
|---|