Skip to content

Commit

Permalink
- Implemented proxy support. Proxy requests contain a full URIs and the
Browse files Browse the repository at this point in the history
  request parser used to throw that information away. Using
  ``urlparse.urlsplit()``, all pieces of the URL are recorded.

- The proxy acheme and netloc/hostname are exposed in the WSGI 
  environment as ``zserver.proxy.scheme`` and ``zserver.proxy.host``.

- Made tests runnable via buildout again

- Get ready for release.
  • Loading branch information
strichter committed Aug 2, 2010
1 parent 067711c commit 38a25f1
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 21 deletions.
10 changes: 8 additions & 2 deletions CHANGES.txt
Expand Up @@ -3,11 +3,17 @@ CHANGES
=======


3.6.3 (unreleased)
3.7.0 (2010-08-01)
------------------

- Nothing changed yet.
- Implemented proxy support. Proxy requests contain a full URIs and the
request parser used to throw that information away. Using
``urlparse.urlsplit()``, all pieces of the URL are recorded.

- The proxy acheme and netloc/hostname are exposed in the WSGI environment as
``zserver.proxy.scheme`` and ``zserver.proxy.host``.

- Made tests runnable via buildout again.

3.6.2 (2010-06-11)
------------------
Expand Down
2 changes: 1 addition & 1 deletion buildout.cfg
Expand Up @@ -10,4 +10,4 @@ eggs = zope.server

[test]
recipe = zc.recipe.testrunner
eggs = zope.server
eggs = zope.server [test]
7 changes: 6 additions & 1 deletion setup.py
Expand Up @@ -26,7 +26,7 @@ def read(*rnames):

setup(
name='zope.server',
version = '3.6.3dev',
version = '3.7.0',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description='Zope Server (Web and FTP)',
Expand Down Expand Up @@ -59,6 +59,11 @@ def read(*rnames):
'zope.publisher',
'zope.security',
],
extras_require = dict([
('test', ['zope.testing',
'zope.i18n',
'zope.component']),
]),
include_package_data = True,
zip_safe = False,
entry_points = """
Expand Down
26 changes: 9 additions & 17 deletions src/zope/server/http/httprequestparser.py
Expand Up @@ -18,6 +18,7 @@
"""
import re
from urllib import unquote
import urlparse

from zope.server.fixedstreamreceiver import FixedStreamReceiver
from zope.server.buffers import OverflowableBuffer
Expand Down Expand Up @@ -172,7 +173,7 @@ def get_header_lines(self):
return r

first_line_re = re.compile (
'([^ ]+) (?:[^ :?#]+://[^ ?#/]*)?([^ ]+)(( HTTP/([0-9.]+))$|$)')
'([^ ]+) ((?:[^ :?#]+://[^ ?#/]*(?:[0-9]{1,5})?)?[^ ]+)(( HTTP/([0-9.]+))$|$)')

def crack_first_line(self):
r = self.first_line
Expand All @@ -186,23 +187,14 @@ def crack_first_line(self):
else:
return None, None, None

path_regex = re.compile (
# path query fragment
r'([^?#]*)(\?[^#]*)?(#.*)?'
)

def split_uri(self):
m = self.path_regex.match (self.uri)
if m.end() != len(self.uri):
raise ValueError("Broken URI")
else:
path, query, self.fragment = m.groups()
if path and '%' in path:
path = unquote(path)
self.path = path
if query:
query = query[1:]
self.query = query
(self.proxy_scheme, self.proxy_netloc, path, self.query, self.fragment) = \
urlparse.urlsplit(self.uri)
if path and '%' in path:
path = unquote(path)
self.path = path
if self.query == '':
self.query = None

def getBodyStream(self):
body_rcv = self.body_rcv
Expand Down
25 changes: 25 additions & 0 deletions src/zope/server/http/tests/test_httprequestparser.py
Expand Up @@ -56,6 +56,8 @@ def testSimpleGET(self):
self.assertEqual(parser.path, '/foobar')
self.assertEqual(parser.command, 'GET')
self.assertEqual(parser.query, None)
self.assertEqual(parser.proxy_scheme, '')
self.assertEqual(parser.proxy_netloc, '')
self.assertEqual(parser.getBodyStream().getvalue(), 'Hello.\n')

def testComplexGET(self):
Expand Down Expand Up @@ -83,6 +85,29 @@ def testComplexGET(self):
'd=b+%2B%2F%3D%26b%3Aint&c+%2B%2F%3D%26c%3Aint=6')
self.assertEqual(parser.getBodyStream().getvalue(), 'Hello mick')

def testProxyGET(self):
data = """\
GET https://example.com:8080/foobar HTTP/8.4
content-length: 7
Hello.
"""
parser = self.parser
self.feed(data)
self.failUnless(parser.completed)
self.assertEqual(parser.version, '8.4')
self.failIf(parser.empty)
self.assertEqual(parser.headers,
{'CONTENT_LENGTH': '7',
})
self.assertEqual(parser.path, '/foobar')
self.assertEqual(parser.command, 'GET')
self.assertEqual(parser.proxy_scheme, 'https')
self.assertEqual(parser.proxy_netloc, 'example.com:8080')
self.assertEqual(parser.command, 'GET')
self.assertEqual(parser.query, None)
self.assertEqual(parser.getBodyStream().getvalue(), 'Hello.\n')

def testDuplicateHeaders(self):
# Ensure that headers with the same key get concatenated as per
# RFC2616.
Expand Down
16 changes: 16 additions & 0 deletions src/zope/server/http/tests/test_wsgiserver.py
Expand Up @@ -116,6 +116,14 @@ def run_once(self, REQUEST):
"""Return whether WSGI app is invoked only once or not"""
return str(bool(REQUEST['wsgi.run_once']))

def proxy_scheme(self, REQUEST):
"""Return the proxy scheme."""
return REQUEST['zserver.proxy.scheme']

def proxy_host(self, REQUEST):
"""Return the proxy host."""
return REQUEST['zserver.proxy.host']

class Tests(PlacelessSetup, unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -252,6 +260,14 @@ def testWSGIRunOnce(self):
status, response_body = self.invokeRequest('/wsgi/run_once')
self.assertEqual('False', response_body)

def testWSGIProxy(self):
status, response_body = self.invokeRequest(
'https://zope.org:8080/wsgi/proxy_scheme')
self.assertEqual('https', response_body)
status, response_body = self.invokeRequest(
'https://zope.org:8080/wsgi/proxy_host')
self.assertEqual('zope.org:8080', response_body)

def test_server_uses_iterable(self):
# Make sure that the task write method isn't called with a
# str or non iterable
Expand Down
9 changes: 9 additions & 0 deletions src/zope/server/http/wsgihttpserver.py
Expand Up @@ -61,6 +61,15 @@ def _constructWSGIEnvironment(self, task):
env['wsgi.multiprocess'] = True
env['wsgi.run_once'] = False
env['wsgi.input'] = task.request_data.getBodyStream()

# Add some proprietary proxy information.
# Note: Derived request parsers might not have these new attributes,
# so fail gracefully.
try:
env['zserver.proxy.scheme'] = task.request_data.proxy_scheme
env['zserver.proxy.host'] = task.request_data.proxy_netloc
except AttributeError:
pass
return env

def executeRequest(self, task):
Expand Down

0 comments on commit 38a25f1

Please sign in to comment.