Skip to content

Fix: Support IPv4/IPv6 dual-stack listening on Windows (Backport from CPython)#43

Open
coolermzb3 wants to merge 1 commit intodanvk:masterfrom
coolermzb3:fix-windows-ipv4
Open

Fix: Support IPv4/IPv6 dual-stack listening on Windows (Backport from CPython)#43
coolermzb3 wants to merge 1 commit intodanvk:masterfrom
coolermzb3:fix-windows-ipv4

Conversation

@coolermzb3
Copy link
Copy Markdown

@coolermzb3 coolermzb3 commented Apr 19, 2026

This PR fixes an issue where the server fails to listen on IPv4 when an IPv6 address is present, particularly on Windows environments. The solution is a backport of the official fixes from the CPython upstream:

cpython#17854
cpython#17865

This is a fresh implementation that resolves conflicts in the inactive PR #25.

Tests passed on Python 3.8 and 3.12 (WSL). While the native Windows test suite encountered environment-specific CRLF mismatches, I have manually verified the functional correctness of the socket binding logic on Windows to ensure it resolves the original issue.

Copilot AI review requested due to automatic review settings April 19, 2026 18:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR backports CPython’s Windows dual-stack listening behavior into RangeHTTPServer’s CLI entrypoint to prevent IPv4 from being unintentionally disabled when IPv6 is involved (notably on Windows).

Changes:

  • Add a custom DualStackServer that attempts to disable IPV6_V6ONLY to enable dual-stack sockets.
  • Wire the custom server into python -m RangeHTTPServer by passing ServerClass=DualStackServer to SimpleHTTPServer.test(...).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +32
class DualStackServer(SimpleHTTPServer.ThreadingHTTPServer):
def server_bind(self):
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
return super().server_bind()
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DualStackServer only tweaks IPV6_V6ONLY but doesn’t set address_family = socket.AF_INET6. As a result, if a user passes an IPv6 bind address like --bind ::, the underlying HTTPServer/ThreadingHTTPServer will still create an AF_INET socket and the bind will fail. To match the CPython dual-stack fix, set the server’s address_family appropriately (and ideally only use the dual-stack server when IPv6 is available via socket.has_dualstack_ipv6()).

Copilot uses AI. Check for mistakes.

args = parser.parse_args()
SimpleHTTPServer.test(HandlerClass=RangeRequestHandler, port=args.port, bind=args.bind)
SimpleHTTPServer.test(HandlerClass=RangeRequestHandler, ServerClass=DualStackServer, port=args.port, bind=args.bind)
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimpleHTTPServer.test(..., ServerClass=DualStackServer, ...) always forces the custom server class even when dual-stack IPv6 isn’t supported on the host. Consider selecting the ServerClass conditionally (e.g., use the dual-stack variant only when socket.has_dualstack_ipv6() is true and the bind address is unspecified/IPv6), and otherwise fall back to the default server class to avoid startup failures on IPv4-only environments.

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +30
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contextlib.suppress(Exception) is overly broad here and can hide real bugs (e.g., programming errors or unexpected socket state). Narrow the suppression to the specific exception types expected from this setsockopt call (typically OSError / AttributeError, depending on platform) so genuine failures still surface.

Suggested change
# suppress exception when protocol is IPv4
with contextlib.suppress(Exception):
# suppress expected platform/socket capability errors when protocol is IPv4
with contextlib.suppress(OSError, AttributeError):

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants