IPv6 is currently widely used. In socketserver library there is no IPv6 servers. Maybe we need IPv6 variant of all servers, something like this:
class TCP6Server(TCPServer):
address_family = socket.AF_INET6
and so on for other classes?
IPv6 is currently widely used. In socketserver library there is no IPv6 servers. Maybe we need IPv6 variant of all servers, something like this:
class TCP6Server(TCPServer):
address_family = socket.AF_INET6
and so on for other classes?
But as you state, socketserver
-based servers already support IPv6 with minimal changes.
You can even modify an existing server instance dynamically:
server = socketserver.TCPServer(("::", 8000), MyHandler)
server.address_family = socket.AF_INET6
That’s right, but the same can be said about UnixStreamServer
and UnixDatagramServer
. We can easy create own, but we have UNIX servers in sdtlib.
So, you suggest duplicating all server classes?
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True )
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)
Another solution could be to modify the BaseServer
class to accept address_family
and modify TCPServer
and UDPServer
to pass address_family
.
Then if the user wants to use IPv6, the server declaration would look like server = socketserver.TCPServer(("::", 9999), MyHandler, address_family=socket.AF_INET6)
.
The default could be set to address_family=socket.AF_INET
to ensure backwards compatibility.
What do you think?
Let me explain.
SOCK_STREAM |
SOCK_DGRAM |
|
---|---|---|
AF_INET |
TCPServer |
UDPServer |
*AF_UNIX* |
UnixStreamServer |
UnixDatagramServer |
AF_INET6 |
TCP6Server |
UDP6Server |
Classes on first 2 lines already exists. I sugest to create classes on third line (indicated italic). Of course in this case we should create ForkingTCP6Server
, ForkingUDP6Server
, ThreadingTCP6Server
and ThreadingUDP6Server
too.
You solution is very nice but there is small problem with backwards compatibility. Current code assume that address_family
is a class variable. For example:
class MyServer(socketserver.TCPServer):
@classmethod
def my_method(cls):
print(cls.address_family)
Maybe this fact is not so important.
IMO it would be ideal if the class could just figure out the address family from the address. Is there code that relies on this? If so, maybe address_family could become a property. If, instead, it’s using self.address_family
, then it’s easy; and the base server currently does exactly that.
Somehow related to the IPv6 discussion - a parameter to control the dual-stack option (at the low-level it’s the IPV6_V6ONLY
socket option) as in the socket.create_server
is missing in the socketserver.
Overall, the exposed classes were initially added in 1995, the same year IPv6 was proposed (standardized in 1998 in RFC 2460, and then updated in 2017 in RFC 8200). RFC 2460 and 8200 are both in the standard tracks, meaning that they are well-established RFCs that we can rely on.
Now that we are seeing more and more IPv6 usages in the wild, I think it would make sense to propose that in the standard library. We already have simple classes that just override the address_family
, so I guess we can also add newest ones as well.
OTOH, the code duplication may look a bit ugly but we might also generate the classes dynamically instead. Or we could make a helper that creates an instance of a TCP server with the correct address family given an address (namely an alternative constructor given as a function) [something similar to socket.create_server
, but that wouldn’t create a socket object, but really a socketserver object].
@guido You’re the main contributor to that module (you essentially wrote it 30 years ago and it hasn’t changed much since then). Do you think it makes sense to create a new interface for IPv6 or do you think we should just stick to TCP servers with a default AF_INET
family? (cc @gpshead as the socket expert as well) Note that the docstring of the module says:
This module tries to capture the various aspects of defining a server:
For socket-based servers:
- address family:
- AF_INET{,6}: IP (Internet Protocol) sockets (default)
- AF_UNIX: Unix domain sockets
- others, e.g. AF_DECNET are conceivable (see <socket.h>)
So one could say that the AF_INET6
construction is missing as it’s being highlighted in the module docstring (I assume that anything else not exposed is being part of “others” and that we don’t need to bother about niche cases). WDYT?
This specific comment from Paul Marks in feature request GH-64414 which is basically what this discussion is about is a good summary.
Sure, we could add IPv6-only server classes with a 6 in the name for TCP and UDP. But that doesn’t provide much value. Anyone can already get that today by writing two lines of code to just subclass and override address_family = socket.AF_INET6
. It’s more of a documentation issue for that feature.
I left a more detailed comment on the issue.
This code is antiquated and I’m not sure new projects should even consider using it, for all the reasons brought up in the GitHub issue Greg pointed to.
It does “support” IPv6 if you simply subclass TCPServer or UDPServer, overriding the address_family
class attribute, literally just like the UNIX variants do.
It’s tempting to just add those (and the corresponding threading and forking variants) to the module and be done. I’d be okay with that. I’d also be okay if we declared the module not worth updating, in which case we should probably (if it isn’t there already) add something to the docs explaining (a) that you can do that easily yourself, and (b) that this doesn’t help much unless you live in an IPv6-only world, because of widespread conventions and assumptions in a world where IPv6 and IPv4 have to live together.
I don’t think it’s a good idea to add any cleverness related to the hybrid world (i.e., the real world) here. Instead, that ought to go in some new module, possibly something 3rd party.
EDIT: Greg already merged a PR that adds exactly what I suggested to the docs.