diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2022-12-06 02:22:12 -0800 |
---|---|---|
committer | Michał Górny <mgorny@gentoo.org> | 2022-12-07 09:55:07 +0100 |
commit | d9be78524e411b9dc68b43eb28cfa11fa634c95f (patch) | |
tree | 834bc1ab9c98ae6187d22148cedb6fd7391ae71a | |
parent | gh-98433: Fix quadratic time idna decoding. (GH-99092) (GH-99222) (diff) | |
download | pypy-gentoo-3.9-7.3.10.tar.gz pypy-gentoo-3.9-7.3.10.tar.bz2 pypy-gentoo-3.9-7.3.10.zip |
gh-100001: Omit control characters in http.server stderr logs. (GH-100002) (#100032)gentoo-3.9-7.3.10
* gh-100001: Omit control characters in http.server stderr logs. (GH-100002)
Replace control characters in http.server.BaseHTTPRequestHandler.log_message with an escaped \xHH sequence to avoid causing problems for the terminal the output is printed to.
(cherry picked from commit d8ab0a4dfa48f881b4ac9ab857d2e9de42f72828)
Co-authored-by: Gregory P. Smith <greg@krypto.org>
* also escape \s (backport of PR #100038).
* add versionadded and remove extra 'to'
Co-authored-by: Gregory P. Smith <greg@krypto.org>
-rw-r--r-- | lib-python/3/http/server.py | 12 | ||||
-rw-r--r-- | lib-python/3/test/test_httpservers.py | 21 |
2 files changed, 31 insertions, 2 deletions
diff --git a/lib-python/3/http/server.py b/lib-python/3/http/server.py index 6bf9084341..cf8933c3db 100644 --- a/lib-python/3/http/server.py +++ b/lib-python/3/http/server.py @@ -93,6 +93,7 @@ import email.utils import html import http.client import io +import itertools import mimetypes import os import posixpath @@ -563,6 +564,11 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): self.log_message(format, *args) + # https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes + _control_char_table = str.maketrans( + {c: fr'\x{c:02x}' for c in itertools.chain(range(0x20), range(0x7f,0xa0))}) + _control_char_table[ord('\\')] = r'\\' + def log_message(self, format, *args): """Log an arbitrary message. @@ -578,12 +584,16 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): The client ip and current date/time are prefixed to every message. + Unicode control characters are replaced with escaped hex + before writing the output to stderr. + """ + message = format % args sys.stderr.write("%s - - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), - format%args)) + message.translate(self._control_char_table))) def version_string(self): """Return the server software version string.""" diff --git a/lib-python/3/test/test_httpservers.py b/lib-python/3/test/test_httpservers.py index 7ba53388f9..c0e1456218 100644 --- a/lib-python/3/test/test_httpservers.py +++ b/lib-python/3/test/test_httpservers.py @@ -26,7 +26,7 @@ import time import datetime import threading from unittest import mock -from io import BytesIO +from io import BytesIO, StringIO import unittest from test import support @@ -984,6 +984,25 @@ class BaseHTTPRequestHandlerTestCase(unittest.TestCase): match = self.HTTPResponseMatch.search(response) self.assertIsNotNone(match) + def test_unprintable_not_logged(self): + # We call the method from the class directly as our Socketless + # Handler subclass overrode it... nice for everything BUT this test. + self.handler.client_address = ('127.0.0.1', 1337) + log_message = BaseHTTPRequestHandler.log_message + with mock.patch.object(sys, 'stderr', StringIO()) as fake_stderr: + log_message(self.handler, '/foo') + log_message(self.handler, '/\033bar\000\033') + log_message(self.handler, '/spam %s.', 'a') + log_message(self.handler, '/spam %s.', '\033\x7f\x9f\xa0beans') + stderr = fake_stderr.getvalue() + self.assertNotIn('\033', stderr) # non-printable chars are caught. + self.assertNotIn('\000', stderr) # non-printable chars are caught. + lines = stderr.splitlines() + self.assertIn('/foo', lines[0]) + self.assertIn(r'/\x1bbar\x00\x1b', lines[1]) + self.assertIn('/spam a.', lines[2]) + self.assertIn('/spam \\x1b\\x7f\\x9f\xa0beans.', lines[3]) + 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]) |