|
|
3ffcc7 |
diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py
|
|
|
3ffcc7 |
index 4064ec2..90b037d 100644
|
|
|
3ffcc7 |
--- a/Lib/DocXMLRPCServer.py
|
|
|
3ffcc7 |
+++ b/Lib/DocXMLRPCServer.py
|
|
|
3ffcc7 |
@@ -20,6 +20,16 @@ from SimpleXMLRPCServer import (SimpleXMLRPCServer,
|
|
|
3ffcc7 |
CGIXMLRPCRequestHandler,
|
|
|
3ffcc7 |
resolve_dotted_attribute)
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
+def _html_escape_quote(s):
|
|
|
3ffcc7 |
+ s = s.replace("&", "&") # Must be done first!
|
|
|
3ffcc7 |
+ s = s.replace("<", "<")
|
|
|
3ffcc7 |
+ s = s.replace(">", ">")
|
|
|
3ffcc7 |
+ s = s.replace('"', """)
|
|
|
3ffcc7 |
+ s = s.replace('\'', "'")
|
|
|
3ffcc7 |
+ return s
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
class ServerHTMLDoc(pydoc.HTMLDoc):
|
|
|
3ffcc7 |
"""Class used to generate pydoc HTML document for a server"""
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
@@ -210,7 +220,8 @@ class XMLRPCDocGenerator:
|
|
|
3ffcc7 |
methods
|
|
|
3ffcc7 |
)
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
- return documenter.page(self.server_title, documentation)
|
|
|
3ffcc7 |
+ title = _html_escape_quote(self.server_title)
|
|
|
3ffcc7 |
+ return documenter.page(title, documentation)
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|
|
3ffcc7 |
"""XML-RPC and documentation request handler class.
|
|
|
3ffcc7 |
diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py
|
|
|
3ffcc7 |
index 80d1803..d464ef8 100644
|
|
|
3ffcc7 |
--- a/Lib/test/test_docxmlrpc.py
|
|
|
3ffcc7 |
+++ b/Lib/test/test_docxmlrpc.py
|
|
|
3ffcc7 |
@@ -1,13 +1,11 @@
|
|
|
3ffcc7 |
from DocXMLRPCServer import DocXMLRPCServer
|
|
|
3ffcc7 |
import httplib
|
|
|
3ffcc7 |
+import re
|
|
|
3ffcc7 |
import sys
|
|
|
3ffcc7 |
from test import test_support
|
|
|
3ffcc7 |
threading = test_support.import_module('threading')
|
|
|
3ffcc7 |
-import time
|
|
|
3ffcc7 |
-import socket
|
|
|
3ffcc7 |
import unittest
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
-PORT = None
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
def make_request_and_skipIf(condition, reason):
|
|
|
3ffcc7 |
# If we skip the test, we have to make a request because the
|
|
|
3ffcc7 |
@@ -23,13 +21,10 @@ def make_request_and_skipIf(condition, reason):
|
|
|
3ffcc7 |
return decorator
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
-def server(evt, numrequests):
|
|
|
3ffcc7 |
+def make_server():
|
|
|
3ffcc7 |
serv = DocXMLRPCServer(("localhost", 0), logRequests=False)
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
try:
|
|
|
3ffcc7 |
- global PORT
|
|
|
3ffcc7 |
- PORT = serv.socket.getsockname()[1]
|
|
|
3ffcc7 |
-
|
|
|
3ffcc7 |
# Add some documentation
|
|
|
3ffcc7 |
serv.set_server_title("DocXMLRPCServer Test Documentation")
|
|
|
3ffcc7 |
serv.set_server_name("DocXMLRPCServer Test Docs")
|
|
|
3ffcc7 |
@@ -56,42 +51,31 @@ def server(evt, numrequests):
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
serv.register_function(add)
|
|
|
3ffcc7 |
serv.register_function(lambda x, y: x-y)
|
|
|
3ffcc7 |
-
|
|
|
3ffcc7 |
- while numrequests > 0:
|
|
|
3ffcc7 |
- serv.handle_request()
|
|
|
3ffcc7 |
- numrequests -= 1
|
|
|
3ffcc7 |
- except socket.timeout:
|
|
|
3ffcc7 |
- pass
|
|
|
3ffcc7 |
- finally:
|
|
|
3ffcc7 |
+ return serv
|
|
|
3ffcc7 |
+ except:
|
|
|
3ffcc7 |
serv.server_close()
|
|
|
3ffcc7 |
- PORT = None
|
|
|
3ffcc7 |
- evt.set()
|
|
|
3ffcc7 |
+ raise
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
class DocXMLRPCHTTPGETServer(unittest.TestCase):
|
|
|
3ffcc7 |
def setUp(self):
|
|
|
3ffcc7 |
- self._threads = test_support.threading_setup()
|
|
|
3ffcc7 |
# Enable server feedback
|
|
|
3ffcc7 |
DocXMLRPCServer._send_traceback_header = True
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
- self.evt = threading.Event()
|
|
|
3ffcc7 |
- threading.Thread(target=server, args=(self.evt, 1)).start()
|
|
|
3ffcc7 |
-
|
|
|
3ffcc7 |
- # wait for port to be assigned
|
|
|
3ffcc7 |
- n = 1000
|
|
|
3ffcc7 |
- while n > 0 and PORT is None:
|
|
|
3ffcc7 |
- time.sleep(0.001)
|
|
|
3ffcc7 |
- n -= 1
|
|
|
3ffcc7 |
+ self.serv = make_server()
|
|
|
3ffcc7 |
+ self.thread = threading.Thread(target=self.serv.serve_forever)
|
|
|
3ffcc7 |
+ self.thread.start()
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
+ PORT = self.serv.server_address[1]
|
|
|
3ffcc7 |
self.client = httplib.HTTPConnection("localhost:%d" % PORT)
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
def tearDown(self):
|
|
|
3ffcc7 |
self.client.close()
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
- self.evt.wait()
|
|
|
3ffcc7 |
-
|
|
|
3ffcc7 |
# Disable server feedback
|
|
|
3ffcc7 |
DocXMLRPCServer._send_traceback_header = False
|
|
|
3ffcc7 |
- test_support.threading_cleanup(*self._threads)
|
|
|
3ffcc7 |
+ self.serv.shutdown()
|
|
|
3ffcc7 |
+ self.thread.join()
|
|
|
3ffcc7 |
+ self.serv.server_close()
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
def test_valid_get_response(self):
|
|
|
3ffcc7 |
self.client.request("GET", "/")
|
|
|
3ffcc7 |
@@ -194,6 +178,25 @@ class DocXMLRPCHTTPGETServer(unittest.TestCase):
|
|
|
3ffcc7 |
self.assertIn("""Try self.add, too.""",
|
|
|
3ffcc7 |
response.read())
|
|
|
3ffcc7 |
|
|
|
3ffcc7 |
+ def test_server_title_escape(self):
|
|
|
3ffcc7 |
+ """Test that the server title and documentation
|
|
|
3ffcc7 |
+ are escaped for HTML.
|
|
|
3ffcc7 |
+ """
|
|
|
3ffcc7 |
+ self.serv.set_server_title('test_title<script>')
|
|
|
3ffcc7 |
+ self.serv.set_server_documentation('test_documentation<script>')
|
|
|
3ffcc7 |
+ self.assertEqual('test_title<script>', self.serv.server_title)
|
|
|
3ffcc7 |
+ self.assertEqual('test_documentation<script>',
|
|
|
3ffcc7 |
+ self.serv.server_documentation)
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
+ generated = self.serv.generate_html_documentation()
|
|
|
3ffcc7 |
+ title = re.search(r'<title>(.+?)</title>', generated).group()
|
|
|
3ffcc7 |
+ documentation = re.search(r'<tt>(.+?)</tt> ', generated).group()
|
|
|
3ffcc7 |
+ self.assertEqual('<title>Python: test_title<script></title>',
|
|
|
3ffcc7 |
+ title)
|
|
|
3ffcc7 |
+ self.assertEqual('<tt>test_documentation<script></tt> ',
|
|
|
3ffcc7 |
+ documentation)
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
+
|
|
|
3ffcc7 |
def test_main():
|
|
|
3ffcc7 |
test_support.run_unittest(DocXMLRPCHTTPGETServer)
|
|
|
3ffcc7 |
|