[Python-checkins] r87320 - in python/branches/release27-maint: Lib/BaseHTTPServer.py Lib/test/test_httpservers.py Misc/ACKS Misc/NEWS

antoine.pitrou python-checkins at python.org
Thu Dec 16 18:11:40 CET 2010


Author: antoine.pitrou
Date: Thu Dec 16 18:11:34 2010
New Revision: 87320

Log:
Merged revisions 87317 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r87317 | antoine.pitrou | 2010-12-16 17:48:36 +0100 (jeu., 16 déc. 2010) | 4 lines
  
  Issue #10714: Limit length of incoming request in http.server to 65536 bytes
  for security reasons.  Initial patch by Ross Lagerwall.
........

(also backported some tests)


Modified:
   python/branches/release27-maint/   (props changed)
   python/branches/release27-maint/Lib/BaseHTTPServer.py
   python/branches/release27-maint/Lib/test/test_httpservers.py
   python/branches/release27-maint/Misc/ACKS
   python/branches/release27-maint/Misc/NEWS

Modified: python/branches/release27-maint/Lib/BaseHTTPServer.py
==============================================================================
--- python/branches/release27-maint/Lib/BaseHTTPServer.py	(original)
+++ python/branches/release27-maint/Lib/BaseHTTPServer.py	Thu Dec 16 18:11:34 2010
@@ -310,7 +310,13 @@
 
         """
         try:
-            self.raw_requestline = self.rfile.readline()
+            self.raw_requestline = self.rfile.readline(65537)
+            if len(self.raw_requestline) > 65536:
+                self.requestline = ''
+                self.request_version = ''
+                self.command = ''
+                self.send_error(414)
+                return
             if not self.raw_requestline:
                 self.close_connection = 1
                 return

Modified: python/branches/release27-maint/Lib/test/test_httpservers.py
==============================================================================
--- python/branches/release27-maint/Lib/test/test_httpservers.py	(original)
+++ python/branches/release27-maint/Lib/test/test_httpservers.py	Thu Dec 16 18:11:34 2010
@@ -484,10 +484,119 @@
                 (res.read(), res.getheader('Content-type'), res.status))
         self.assertEqual(os.environ['SERVER_SOFTWARE'], signature)
 
+
+class SocketlessRequestHandler(SimpleHTTPRequestHandler):
+    def __init__(self):
+        self.get_called = False
+        self.protocol_version = "HTTP/1.1"
+
+    def do_GET(self):
+        self.get_called = True
+        self.send_response(200)
+        self.send_header('Content-Type', 'text/html')
+        self.end_headers()
+        self.wfile.write(b'<html><body>Data</body></html>\r\n')
+
+    def log_message(self, format, *args):
+        pass
+
+class RejectingSocketlessRequestHandler(SocketlessRequestHandler):
+    def handle_expect_100(self):
+        self.send_error(417)
+        return False
+
+class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
+    """Test the functionaility of the BaseHTTPServer.
+
+       Test the support for the Expect 100-continue header.
+       """
+
+    HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK')
+
+    def setUp (self):
+        self.handler = SocketlessRequestHandler()
+
+    def send_typical_request(self, message):
+        input = StringIO(message)
+        output = StringIO()
+        self.handler.rfile = input
+        self.handler.wfile = output
+        self.handler.handle_one_request()
+        output.seek(0)
+        return output.readlines()
+
+    def verify_get_called(self):
+        self.assertTrue(self.handler.get_called)
+
+    def verify_expected_headers(self, headers):
+        for fieldName in b'Server: ', b'Date: ', b'Content-Type: ':
+            self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1)
+
+    def verify_http_server_response(self, response):
+        match = self.HTTPResponseMatch.search(response)
+        self.assertTrue(match is not None)
+
+    def test_http_1_1(self):
+        result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n')
+        self.verify_http_server_response(result[0])
+        self.verify_expected_headers(result[1:-1])
+        self.verify_get_called()
+        self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+
+    def test_http_1_0(self):
+        result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n')
+        self.verify_http_server_response(result[0])
+        self.verify_expected_headers(result[1:-1])
+        self.verify_get_called()
+        self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+
+    def test_http_0_9(self):
+        result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n')
+        self.assertEqual(len(result), 1)
+        self.assertEqual(result[0], b'<html><body>Data</body></html>\r\n')
+        self.verify_get_called()
+
+    def test_with_continue_1_0(self):
+        result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n')
+        self.verify_http_server_response(result[0])
+        self.verify_expected_headers(result[1:-1])
+        self.verify_get_called()
+        self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
+
+    def test_request_length(self):
+        # Issue #10714: huge request lines are discarded, to avoid Denial
+        # of Service attacks.
+        result = self.send_typical_request(b'GET ' + b'x' * 65537)
+        self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
+        self.assertFalse(self.handler.get_called)
+
+class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
+    """ Test url parsing """
+    def setUp(self):
+        self.translated = os.getcwd()
+        self.translated = os.path.join(self.translated, 'filename')
+        self.handler = SocketlessRequestHandler()
+
+    def test_query_arguments(self):
+        path = self.handler.translate_path('/filename')
+        self.assertEqual(path, self.translated)
+        path = self.handler.translate_path('/filename?foo=bar')
+        self.assertEqual(path, self.translated)
+        path = self.handler.translate_path('/filename?a=b&spam=eggs#zot')
+        self.assertEqual(path, self.translated)
+
+    def test_start_with_double_slash(self):
+        path = self.handler.translate_path('//filename')
+        self.assertEqual(path, self.translated)
+        path = self.handler.translate_path('//filename?foo=bar')
+        self.assertEqual(path, self.translated)
+
+
 def test_main(verbose=None):
     try:
         cwd = os.getcwd()
         test_support.run_unittest(BaseHTTPRequestHandlerTestCase,
+                                  SimpleHTTPRequestHandlerTestCase,
                                   BaseHTTPServerTestCase,
                                   SimpleHTTPServerTestCase,
                                   CGIHTTPServerTestCase

Modified: python/branches/release27-maint/Misc/ACKS
==============================================================================
--- python/branches/release27-maint/Misc/ACKS	(original)
+++ python/branches/release27-maint/Misc/ACKS	Thu Dec 16 18:11:34 2010
@@ -447,6 +447,7 @@
 Ivan Krstić
 Andrew Kuchling
 Vladimir Kushnir
+Ross Lagerwall
 Cameron Laird
 Łukasz Langa
 Tino Lange

Modified: python/branches/release27-maint/Misc/NEWS
==============================================================================
--- python/branches/release27-maint/Misc/NEWS	(original)
+++ python/branches/release27-maint/Misc/NEWS	Thu Dec 16 18:11:34 2010
@@ -22,6 +22,9 @@
 Library
 -------
 
+- Issue #10714: Limit length of incoming request in http.server to 65536 bytes
+  for security reasons.  Initial patch by Ross Lagerwall.
+
 - Issue #9558: Fix distutils.command.build_ext with VS 8.0.
 
 - Issue #10695: passing the port as a string value to telnetlib no longer


More information about the Python-checkins mailing list