~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web/proxy.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
 
 
5
"""Simplistic HTTP proxy support.
 
6
 
 
7
This comes in two main variants - the Proxy and the ReverseProxy.
 
8
 
 
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.
 
12
 
 
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.
 
16
 
 
17
Normally, a Proxy is used on the client end of an Internet connection, while a
 
18
ReverseProxy is used on the server end.
 
19
"""
 
20
 
 
21
# twisted imports
 
22
from twisted.internet import reactor, protocol
 
23
from twisted.web import resource, server, http
 
24
 
 
25
# system imports
 
26
import urlparse
 
27
 
 
28
 
 
29
class ProxyClient(http.HTTPClient):
 
30
    """Used by ProxyClientFactory to implement a simple web proxy."""
 
31
 
 
32
    def __init__(self, command, rest, version, headers, data, father):
 
33
        self.father = father
 
34
        self.command = command
 
35
        self.rest = rest
 
36
        if headers.has_key("proxy-connection"):
 
37
            del headers["proxy-connection"]
 
38
        headers["connection"] = "close"
 
39
        self.headers = headers
 
40
        self.data = data
 
41
 
 
42
    def connectionMade(self):
 
43
        self.sendCommand(self.command, self.rest)
 
44
        for header, value in self.headers.items():
 
45
            self.sendHeader(header, value)
 
46
        self.endHeaders()
 
47
        self.transport.write(self.data)
 
48
 
 
49
    def handleStatus(self, version, code, message):
 
50
        self.father.transport.write("%s %s %s\r\n" % (version, code, message))
 
51
 
 
52
    def handleHeader(self, key, value):
 
53
        self.father.transport.write("%s: %s\r\n" % (key, value))
 
54
 
 
55
    def handleEndHeaders(self):
 
56
        self.father.transport.write("\r\n")
 
57
    
 
58
    def handleResponsePart(self, buffer):
 
59
        self.father.transport.write(buffer)
 
60
 
 
61
    def handleResponseEnd(self):
 
62
        self.transport.loseConnection()
 
63
        self.father.channel.transport.loseConnection()
 
64
 
 
65
 
 
66
class ProxyClientFactory(protocol.ClientFactory):
 
67
    """Used by ProxyRequest to implement a simple web proxy."""
 
68
 
 
69
    protocol = ProxyClient
 
70
 
 
71
    def __init__(self, command, rest, version, headers, data, father):
 
72
        self.father = father
 
73
        self.command = command
 
74
        self.rest = rest
 
75
        self.headers = headers
 
76
        self.data = data
 
77
        self.version = version
 
78
 
 
79
 
 
80
    def buildProtocol(self, addr):
 
81
        return self.protocol(self.command, self.rest, self.version,
 
82
                             self.headers, self.data, self.father)
 
83
 
 
84
 
 
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>''')
 
90
 
 
91
 
 
92
 
 
93
class ProxyRequest(http.Request):
 
94
    """Used by Proxy to implement a simple web proxy."""
 
95
 
 
96
    protocols = {'http': ProxyClientFactory}
 
97
    ports = {'http': 80}
 
98
 
 
99
    def process(self):
 
100
        parsed = urlparse.urlparse(self.uri)
 
101
        protocol = parsed[0]
 
102
        host = parsed[1]
 
103
        port = self.ports[protocol]
 
104
        if ':' in host:
 
105
            host, port = host.split(':')
 
106
            port = int(port)
 
107
        rest = urlparse.urlunparse(('','')+parsed[2:])
 
108
        if not rest:
 
109
            rest = rest+'/'
 
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,
 
117
                               s, self)
 
118
        reactor.connectTCP(host, port, clientFactory)
 
119
 
 
120
 
 
121
class Proxy(http.HTTPChannel):
 
122
    """This class implements a simple web proxy.
 
123
 
 
124
    Since it inherits from twisted.protocols.http.HTTPChannel, to use it you
 
125
    should do something like this::
 
126
 
 
127
        from twisted.web import http
 
128
        f = http.HTTPFactory()
 
129
        f.protocol = Proxy
 
130
 
 
131
    Make the HTTPFactory a listener on a port as per usual, and you have
 
132
    a fully-functioning web proxy!
 
133
    """
 
134
 
 
135
    requestFactory = ProxyRequest
 
136
 
 
137
 
 
138
class ReverseProxyRequest(http.Request):
 
139
    """Used by ReverseProxy to implement a simple reverse proxy."""
 
140
 
 
141
    def process(self):
 
142
        self.received_headers['host'] = self.factory.host
 
143
        clientFactory = ProxyClientFactory(self.method, self.uri,
 
144
                                            self.clientproto,
 
145
                                            self.getAllHeaders(), 
 
146
                                            self.content.read(), self)
 
147
        reactor.connectTCP(self.factory.host, self.factory.port,
 
148
                           clientFactory)
 
149
 
 
150
class ReverseProxy(http.HTTPChannel):
 
151
    """Implements a simple reverse proxy.
 
152
 
 
153
    For details of usage, see the file examples/proxy.py"""
 
154
 
 
155
    requestFactory = ReverseProxyRequest
 
156
 
 
157
 
 
158
class ReverseProxyResource(resource.Resource):
 
159
    """Resource that renders the results gotten from another server
 
160
 
 
161
    Put this resource in the tree to cause everything below it to be relayed
 
162
    to a different server.
 
163
    """
 
164
 
 
165
    def __init__(self, host, port, path):
 
166
        resource.Resource.__init__(self)
 
167
        self.host = host
 
168
        self.port = port
 
169
        self.path = path
 
170
 
 
171
    def getChild(self, path, request):
 
172
        return ReverseProxyResource(self.host, self.port, self.path+'/'+path)
 
173
 
 
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]
 
178
        if qs:
 
179
            rest = self.path + '?' + qs
 
180
        else:
 
181
            rest = self.path
 
182
        clientFactory = ProxyClientFactory(request.method, rest, 
 
183
                                     request.clientproto, 
 
184
                                     request.getAllHeaders(),
 
185
                                     request.content.read(),
 
186
                                     request)
 
187
        reactor.connectTCP(self.host, self.port, clientFactory)
 
188
        return server.NOT_DONE_YET