Jason R. Coombs <jar...@jaraco.com> added the comment:
First, a quick primer in IP: - Addresses are written as XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX, but any single span of zeros can be written as ::, so `::` is all zeros and `::1` is the same as 0000:0000:0000:0000:0000:0000:0000:0001. - ::1 is the local host (the some machine as where the code is running), equivalent to 127.0.0.1 in IPv4. - To listen on all interfaces, the socket library expects the system to bind to 0.0.0.0 (IPv4) or :: (IPv6). - When specified in a URL, an IPv6 address must be wrapped in [] to distinguish the `:` characters from the port separator. For example, http://[::1]:8000/ specifies connect to localhost over IPv6 on port 8000. - If the system supports dual-stack IPv4 over IPv6, all IPv4 addresses are mapped to a specific IPv6 subnet, so binding/listening on IPv6 often allows a client to connect to IPv4. - Even if the server is listening on all interfaces (0.0.0.0/::), the client must specify an internet address that will reach that address. As a result of this last point, it's not possible for a server like http.server to reliably know what address a client would be able to use to connect to the server. That is, if the server is bound on all interfaces, a local client could connect over localhost/127.0.0.1/::1 (assuming that interface exist, which it doesn't sometimes) or to another address assigned by the host, e.g. 2601:547:501:6ba:d1e6:300d:7e83:6b6f. A client on another host, however, would not be able to use localhost to connect to the server. It _must_ use an address that's both assigned to the server's host, bound by the server, and routeable to/from the client (i.e. not blocked by a firewall). Prior to Python 3.8, the default behavior was to bind to all interfaces on IPv4 only, which was unnecessarily limiting, but was subject to the same unexpected behavior: ``` draft $ python3.7 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... ``` The URL there `http://0.0.0.0:8000/` has the same failure mode as the one described above. One cannot browse to that address, but must replace `0.0.0.0` with `localhost` or `127.0.0.1` (to connect from localhost) or replace with a routable address to connect from another host. The only difference is that with Python 3.8, now IPv6 is honored. Note if one passes `localhost` or `127.0.0.1` or `::1` as the bind parameter, the URL indicated would work: ``` draft $ python -m http.server --bind localhost Serving HTTP on ::1 port 8000 (http://[::1]:8000/) ... ``` Since it's not possible in general to supply the URL a client would need to connect to the server, it's difficult to reliably provide a useful URL. Some web servers do apply [a heuristic](https://github.com/jaraco/portend/blob/754c37046d86d178d20faa8dbfe910482d79bdff/portend.py#L27-L46) that translates "all addresses" to a "localhost" address, and Python stdlib could implement that heuristic. > On 3.7.X I was able to use it as described in the docs and it would default > to whatever IP address was available. That behavior should be the same, except that it should now bind to both IPv6 and IPv4. If you previously ran without any parameters, it would bind to all interfaces on IPv4. Now it binds on all interfaces on IPv6, which should be backward compatible in dual-stack environments like Windows. You just have to translate `[::]` to `localhost` instead of translating `0.0.0.0` to `localhost`. When I tested your findings on macOS, everything worked as I expected. I launched the server with `python -m http.server`, and the site could be reached on http://localhost:8000/ and http://127.0.0.1:8000 and http://[::1]:8000/. Nevertheless, when I tried the same thing on my Windows machine, I got a different outcome. The server bound to [::0] but was unreachable on http://127.0.0.1:8000. That was unexpected, and I'll try to ascertain why the dual-stack behavior isn't working as I'd expect. ---------- nosy: -SilentGhost stage: needs patch -> _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue39211> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com