1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
"""Simplistic HTTP proxy support.
7
This comes in two main variants - the Proxy and the ReverseProxy.
9
When a Proxy is in use, a browser trying to connect to a server (say,
10
www.yahoo.com) will be intercepted by the Proxy, and the proxy will covertly
11
connect to the server, and return the result.
13
When a ReverseProxy is in use, the client connects directly to the ReverseProxy
14
(say, www.yahoo.com) which farms off the request to one of a pool of servers,
15
and returns the result.
17
Normally, a Proxy is used on the client end of an Internet connection, while a
18
ReverseProxy is used on the server end.
22
from twisted.internet import reactor, protocol
23
from twisted.web import resource, server, http
29
class ProxyClient(http.HTTPClient):
30
"""Used by ProxyClientFactory to implement a simple web proxy."""
32
def __init__(self, command, rest, version, headers, data, father):
34
self.command = command
36
if headers.has_key("proxy-connection"):
37
del headers["proxy-connection"]
38
headers["connection"] = "close"
39
self.headers = headers
42
def connectionMade(self):
43
self.sendCommand(self.command, self.rest)
44
for header, value in self.headers.items():
45
self.sendHeader(header, value)
47
self.transport.write(self.data)
49
def handleStatus(self, version, code, message):
50
self.father.transport.write("%s %s %s\r\n" % (version, code, message))
52
def handleHeader(self, key, value):
53
self.father.transport.write("%s: %s\r\n" % (key, value))
55
def handleEndHeaders(self):
56
self.father.transport.write("\r\n")
58
def handleResponsePart(self, buffer):
59
self.father.transport.write(buffer)
61
def handleResponseEnd(self):
62
self.transport.loseConnection()
63
self.father.channel.transport.loseConnection()
66
class ProxyClientFactory(protocol.ClientFactory):
67
"""Used by ProxyRequest to implement a simple web proxy."""
69
protocol = ProxyClient
71
def __init__(self, command, rest, version, headers, data, father):
73
self.command = command
75
self.headers = headers
77
self.version = version
80
def buildProtocol(self, addr):
81
return self.protocol(self.command, self.rest, self.version,
82
self.headers, self.data, self.father)
85
def clientConnectionFailed(self, connector, reason):
86
self.father.transport.write("HTTP/1.0 501 Gateway error\r\n")
87
self.father.transport.write("Content-Type: text/html\r\n")
88
self.father.transport.write("\r\n")
89
self.father.transport.write('''<H1>Could not connect</H1>''')
93
class ProxyRequest(http.Request):
94
"""Used by Proxy to implement a simple web proxy."""
96
protocols = {'http': ProxyClientFactory}
100
parsed = urlparse.urlparse(self.uri)
103
port = self.ports[protocol]
105
host, port = host.split(':')
107
rest = urlparse.urlunparse(('','')+parsed[2:])
110
class_ = self.protocols[protocol]
111
headers = self.getAllHeaders().copy()
112
if not headers.has_key('host'):
113
headers['host'] = host
114
self.content.seek(0, 0)
115
s = self.content.read()
116
clientFactory = class_(self.method, rest, self.clientproto, headers,
118
reactor.connectTCP(host, port, clientFactory)
121
class Proxy(http.HTTPChannel):
122
"""This class implements a simple web proxy.
124
Since it inherits from twisted.protocols.http.HTTPChannel, to use it you
125
should do something like this::
127
from twisted.web import http
128
f = http.HTTPFactory()
131
Make the HTTPFactory a listener on a port as per usual, and you have
132
a fully-functioning web proxy!
135
requestFactory = ProxyRequest
138
class ReverseProxyRequest(http.Request):
139
"""Used by ReverseProxy to implement a simple reverse proxy."""
142
self.received_headers['host'] = self.factory.host
143
clientFactory = ProxyClientFactory(self.method, self.uri,
145
self.getAllHeaders(),
146
self.content.read(), self)
147
reactor.connectTCP(self.factory.host, self.factory.port,
150
class ReverseProxy(http.HTTPChannel):
151
"""Implements a simple reverse proxy.
153
For details of usage, see the file examples/proxy.py"""
155
requestFactory = ReverseProxyRequest
158
class ReverseProxyResource(resource.Resource):
159
"""Resource that renders the results gotten from another server
161
Put this resource in the tree to cause everything below it to be relayed
162
to a different server.
165
def __init__(self, host, port, path):
166
resource.Resource.__init__(self)
171
def getChild(self, path, request):
172
return ReverseProxyResource(self.host, self.port, self.path+'/'+path)
174
def render(self, request):
175
request.received_headers['host'] = self.host
176
request.content.seek(0, 0)
177
qs = urlparse.urlparse(request.uri)[4]
179
rest = self.path + '?' + qs
182
clientFactory = ProxyClientFactory(request.method, rest,
184
request.getAllHeaders(),
185
request.content.read(),
187
reactor.connectTCP(self.host, self.port, clientFactory)
188
return server.NOT_DONE_YET