Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

bpo-24209: In http.server script, rely on getaddrinfo to bind to preferred address based on the bind parameter. #11767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 7, 2019
30 changes: 20 additions & 10 deletions 30 Lib/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1224,24 +1224,34 @@ def run_cgi(self):
self.log_message("CGI script exited OK")


def _get_best_family(*address):
infos = socket.getaddrinfo(
*address,
type=socket.SOCK_STREAM,
flags=socket.AI_PASSIVE,
)
family, type, proto, canonname, sockaddr = next(iter(infos))
return family, sockaddr


def test(HandlerClass=BaseHTTPRequestHandler,
ServerClass=ThreadingHTTPServer,
protocol="HTTP/1.0", port=8000, bind=""):
protocol="HTTP/1.0", port=8000, bind=None):
"""Test the HTTP request handler class.

This runs an HTTP server on port 8000 (or the port argument).

"""
server_address = (bind, port)

if ':' in bind:
ServerClass.address_family = socket.AF_INET6
ServerClass.address_family, addr = _get_best_family(bind, port)

HandlerClass.protocol_version = protocol
with ServerClass(server_address, HandlerClass) as httpd:
sa = httpd.socket.getsockname()
serve_message = "Serving HTTP on {host} port {port} (http://{host}:{port}/) ..."
print(serve_message.format(host=sa[0], port=sa[1]))
with ServerClass(addr, HandlerClass) as httpd:
host, port = httpd.socket.getsockname()[:2]
url_host = f'[{host}]' if ':' in host else host
print(
f"Serving HTTP on {host} port {port} "
f"(http://{url_host}:{port}/) ..."
)
try:
httpd.serve_forever()
except KeyboardInterrupt:
Expand All @@ -1254,7 +1264,7 @@ def test(HandlerClass=BaseHTTPRequestHandler,
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='Run as CGI Server')
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
parser.add_argument('--bind', '-b', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('--directory', '-d', default=os.getcwd(),
Expand Down
68 changes: 55 additions & 13 deletions 68 Lib/test/test_httpservers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1118,21 +1118,63 @@ def test_all(self):


class ScriptTestCase(unittest.TestCase):

def mock_server_class(self):
return mock.MagicMock(
return_value=mock.MagicMock(
__enter__=mock.MagicMock(
return_value=mock.MagicMock(
socket=mock.MagicMock(
getsockname=lambda: ('', 0),
),
),
),
),
)

@mock.patch('builtins.print')
def test_server_test_unspec(self, _):
mock_server = self.mock_server_class()
server.test(ServerClass=mock_server, bind=None)
self.assertIn(
mock_server.address_family,
(socket.AF_INET6, socket.AF_INET),
)

@mock.patch('builtins.print')
def test_server_test_localhost(self, _):
mock_server = self.mock_server_class()
server.test(ServerClass=mock_server, bind="localhost")
self.assertIn(
mock_server.address_family,
(socket.AF_INET6, socket.AF_INET),
)

ipv6_addrs = (
"::",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"::1",
)

ipv4_addrs = (
"0.0.0.0",
"8.8.8.8",
"127.0.0.1",
)

@mock.patch('builtins.print')
def test_server_test_ipv6(self, _):
mock_server = mock.MagicMock()
server.test(ServerClass=mock_server, bind="::")
self.assertEqual(mock_server.address_family, socket.AF_INET6)

mock_server.reset_mock()
server.test(ServerClass=mock_server,
bind="2001:0db8:85a3:0000:0000:8a2e:0370:7334")
self.assertEqual(mock_server.address_family, socket.AF_INET6)

mock_server.reset_mock()
server.test(ServerClass=mock_server,
bind="::1")
self.assertEqual(mock_server.address_family, socket.AF_INET6)
for bind in self.ipv6_addrs:
mock_server = self.mock_server_class()
server.test(ServerClass=mock_server, bind=bind)
self.assertEqual(mock_server.address_family, socket.AF_INET6)

@mock.patch('builtins.print')
def test_server_test_ipv4(self, _):
for bind in self.ipv4_addrs:
mock_server = self.mock_server_class()
server.test(ServerClass=mock_server, bind=bind)
self.assertEqual(mock_server.address_family, socket.AF_INET)


def test_main(verbose=None):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In http.server script, rely on getaddrinfo to bind to preferred address based on the bind parameter. Now default bind or binding to a name may bind to IPv6 or dual-stack, depending on the environment.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.