root / trunk / twisted / web2 / vhost.py

Revision 17544, 7.6 kB (checked in by dreid, 3 years ago)

Docstrings for various userfacing methods and classes.

This also contains a few extra lines for readability sake.

Reviewer: MFen
Author: dreid
Fixes #1919

Line 
1 # -*- test-case-name: twisted.web2.test.test_vhost -*-
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """I am a virtual hosts implementation.
6 """
7
8 # System Imports
9 import urlparse
10 from zope.interface import implements
11 import urllib
12 import warnings
13
14 from twisted.internet import address
15 from twisted.python import log
16
17 # Sibling Imports
18 from twisted.web2 import resource
19 from twisted.web2 import responsecode
20 from twisted.web2 import iweb
21 from twisted.web2 import http
22
23 class NameVirtualHost(resource.Resource):
24     """Resource in charge of dispatching requests to other resources based on
25     the value of the HTTP 'Host' header.
26       
27     @param supportNested: If True domain segments will be chopped off until the
28         TLD is reached or a matching virtual host is found. (In which case the
29         child resource can do its own more specific virtual host lookup.)
30     """
31    
32     supportNested = True
33
34     def __init__(self, default=None):
35         """
36         @param default: The default resource to be served when encountering an
37             unknown hostname.
38         @type default: L{twisted.web2.iweb.IResource} or C{None}
39         """
40         resource.Resource.__init__(self)
41         self.hosts = {}
42        
43         self.default = default
44        
45     def addHost(self, name, resrc):
46         """Add a host to this virtual host. - The Fun Stuff(TM)
47
48         This associates a host named 'name' with a resource 'resrc'::
49
50             nvh.addHost('nevow.com', nevowDirectory)
51             nvh.addHost('divmod.org', divmodDirectory)
52             nvh.addHost('twistedmatrix.com', twistedMatrixDirectory)
53
54         I told you that was fun.
55
56         @param name: The FQDN to be matched to the 'Host' header.
57         @type name: C{str}
58
59         @param resrc: The L{twisted.web2.iweb.IResource} to be served as the
60             given hostname.
61         @type resource: L{twisted.web2.iweb.IResource}
62         """
63         self.hosts[name] = resrc
64
65     def removeHost(self, name):
66         """Remove the given host.
67         @param name: The FQDN to remove.
68         @type name: C{str}
69         """
70         del self.hosts[name]
71
72     def locateChild(self, req, segments):
73         """It's a NameVirtualHost, do you know where your children are?
74         
75         This uses locateChild magic so you don't have to mutate the request.
76         """
77
78         host = req.host.lower()
79        
80         if self.supportNested:
81             while not self.hosts.has_key(host) and len(host.split('.')) > 1:
82                 host = '.'.join(host.split('.')[1:])
83
84         # Default being None is okay, it'll turn into a 404
85         return self.hosts.get(host, self.default), segments
86
87
88 class AutoVHostURIRewrite(object):
89     """
90     I do request mangling to insure that children know what host they are being
91     accessed from behind apache2.
92
93     Usage:
94
95         - Twisted::
96
97             root = MyResource()
98             vur = vhost.AutoVHostURIRewrite(root)
99
100         - Apache2::
101
102             <Location /whatever/>
103               ProxyPass http://localhost:8538/
104               RequestHeader set X-App-Location /whatever/
105             </Location>
106
107         If the trailing / is ommitted in the second argument to ProxyPass
108         VHostURIRewrite will return a 404 response code.
109
110         If proxying HTTPS, add this to the Apache config::
111
112             RequestHeader set X-App-Scheme https
113     """
114     implements(iweb.IResource)
115
116     def __init__(self, resource, sendsRealHost=False):
117         """
118         @param resource: The resource to serve after mutating the request.
119         @type resource: L{twisted.web2.iweb.IResource}
120         
121         @param sendsRealHost: If True then the proxy will be expected to send the
122             HTTP 'Host' header that was sent by the requesting client.
123         @type sendsRealHost: C{bool}
124         """
125
126         self.resource=resource
127         self.sendsRealHost = sendsRealHost
128        
129     def renderHTTP(self, req):
130         return http.Response(responsecode.NOT_FOUND)
131
132     def locateChild(self, req, segments):
133         scheme = req.headers.getRawHeaders('x-app-scheme')
134
135         if self.sendsRealHost:
136             host = req.headers.getRawHeaders('host')
137         else:
138             host = req.headers.getRawHeaders('x-forwarded-host')
139
140         app_location = req.headers.getRawHeaders('x-app-location')
141         remote_ip = req.headers.getRawHeaders('x-forwarded-for')
142
143         if not (host and remote_ip):
144             if not host:
145                 warnings.warn(
146                     ("No host was obtained either from Host or "
147                      "X-Forwarded-Host headers.  If your proxy does not "
148                      "send either of these headers use VHostURIRewrite. "
149                      "If your proxy sends the real host as the Host header "
150                      "use "
151                      "AutoVHostURIRewrite(resrc, sendsRealHost=True)"))
152
153             # some header unspecified => Error
154             raise http.HTTPError(responsecode.BAD_REQUEST)
155         host = host[0]
156         remote_ip = remote_ip[0]
157         if app_location:
158             app_location = app_location[0]
159         else:
160             app_location = '/'
161         if scheme:
162             scheme = scheme[0]
163         else:
164             scheme='http'
165        
166         req.host, req.port = http.splitHostPort(scheme, host)
167         req.scheme = scheme
168        
169         req.remoteAddr = address.IPv4Address('TCP', remote_ip, 0)
170            
171         req.prepath = app_location[1:].split('/')[:-1]
172         req.path = '/'+('/'.join([urllib.quote(s, '') for s in (req.prepath + segments)]))
173        
174         return self.resource, segments
175        
176 class VHostURIRewrite(object):
177     """
178     I do request mangling to insure that children know what host they are being
179     accessed from behind mod_proxy.
180
181     Usage:
182
183         - Twisted::
184
185             root = MyResource()
186             vur = vhost.VHostURIRewrite(uri='http://hostname:port/path', resource=root)
187             server.Site(vur)
188
189         - Apache::
190
191             <VirtualHost hostname:port>
192                 ProxyPass /path/ http://localhost:8080/
193                 Servername hostname
194             </VirtualHost>
195
196         If the trailing / is ommitted in the second argument to ProxyPass
197         VHostURIRewrite will return a 404 response code.
198
199         uri must be a fully specified uri complete with scheme://hostname/path/
200     """
201
202     implements(iweb.IResource)
203
204     def __init__(self, uri, resource):
205         """
206         @param uri: The URI to be used for mutating the request.  This MUST
207             include scheme://hostname/path.
208         @type uri: C{str}
209         
210         @param resource: The resource to serve after mutating the request.
211         @type resource: L{twisted.web2.iweb.IResource}
212         """
213
214         self.resource = resource
215        
216         (self.scheme, self.host, self.path,
217          params, querystring, fragment) = urlparse.urlparse(uri)
218         if params or querystring or fragment:
219             raise ValueError("Must not specify params, query args, or fragment to VHostURIRewrite")
220         self.path = map(urllib.unquote, self.path[1:].split('/'))[:-1]
221         self.host, self.port = http.splitHostPort(self.scheme, self.host)
222        
223     def renderHTTP(self, req):
224         return http.Response(responsecode.NOT_FOUND)
225
226     def locateChild(self, req, segments):
227         req.scheme = self.scheme
228         req.host = self.host
229         req.port = self.port
230         req.prepath=self.path[:]
231         req.path = '/'+('/'.join([urllib.quote(s, '') for s in (req.prepath + segments)]))
232         # print req.prepath, segments, req.postpath, req.path
233         
234         return self.resource, segments
235
236 __all__ = ['VHostURIRewrite', 'AutoVHostURIRewrite', 'NameVirtualHost']
Note: See TracBrowser for help on using the browser.