diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index 56f4c0a0d772f4..e605f7b8b14172 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -368,6 +368,8 @@ HTTPConnection Objects this is called automatically when making a request if the client does not already have a connection. + .. audit-event:: http.client.connect self,host,port http.client.HTTPConnection.connect + .. method:: HTTPConnection.close() @@ -437,6 +439,8 @@ also send your request step by step, by using the four functions below. :meth:`endheaders` method has been called and before :meth:`getresponse` is called. + .. audit-event:: http.client.send self,data http.client.HTTPConnection.send + .. _httpresponse-objects: diff --git a/Lib/http/client.py b/Lib/http/client.py index c526380dcabfc9..4b1f692844474f 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -74,6 +74,7 @@ import io import re import socket +import sys import collections.abc from urllib.parse import urlsplit @@ -931,6 +932,7 @@ def _tunnel(self): def connect(self): """Connect to the host and port specified in __init__.""" + sys.audit("http.client.connect", self, self.host, self.port) self.sock = self._create_connection( (self.host,self.port), self.timeout, self.source_address) self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) @@ -978,8 +980,10 @@ def send(self, data): break if encode: datablock = datablock.encode("iso-8859-1") + sys.audit("http.client.send", self, datablock) self.sock.sendall(datablock) return + sys.audit("http.client.send", self, data) try: self.sock.sendall(data) except TypeError: diff --git a/Lib/test/audit-tests.py b/Lib/test/audit-tests.py index 8e66594e52429b..2addf9762eae49 100644 --- a/Lib/test/audit-tests.py +++ b/Lib/test/audit-tests.py @@ -341,6 +341,24 @@ def hook(event, args): gc.get_referents(y) +def test_http_client(): + import http.client + + def hook(event, args): + if event.startswith("http.client."): + print(event, *args[1:]) + + sys.addaudithook(hook) + + conn = http.client.HTTPConnection('www.python.org') + try: + conn.request('GET', '/') + except OSError: + print('http.client.send', '[cannot send]') + finally: + conn.close() + + if __name__ == "__main__": from test.support import suppress_msvcrt_asserts diff --git a/Lib/test/test_audit.py b/Lib/test/test_audit.py index 58180e147a49a3..456a5daceb9f10 100644 --- a/Lib/test/test_audit.py +++ b/Lib/test/test_audit.py @@ -130,6 +130,20 @@ def test_gc(self): ["gc.get_objects", "gc.get_referrers", "gc.get_referents"] ) + def test_http(self): + import_helper.import_module("http.client") + returncode, events, stderr = self.run_python("test_http_client") + if returncode: + self.fail(stderr) + + if support.verbose: + print(*events, sep='\n') + self.assertEqual(events[0][0], "http.client.connect") + self.assertEqual(events[0][2], "www.python.org 80") + self.assertEqual(events[1][0], "http.client.send") + if events[1][2] != '[cannot send]': + self.assertIn('HTTP', events[1][2]) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Security/2020-07-04-22-14-46.bpo-37363.NDjHNw.rst b/Misc/NEWS.d/next/Security/2020-07-04-22-14-46.bpo-37363.NDjHNw.rst new file mode 100644 index 00000000000000..539084836dc4e5 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-07-04-22-14-46.bpo-37363.NDjHNw.rst @@ -0,0 +1 @@ +Add audit events to the :mod:`http.client` module. \ No newline at end of file