Browse Source

Fixed #7735 -- Added support for IPv6 adresses to runserver and testserver management command. Thanks to Jason Alonso and Łukasz Rekucki for the report and initial patches.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14711 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Jannis Leidel 14 năm trước cách đây
mục cha
commit
6a32e253f6

+ 28 - 12
django/core/management/commands/runserver.py

@@ -1,14 +1,21 @@
 from optparse import make_option
 import os
+import re
 import sys
+import socket
 
 from django.core.management.base import BaseCommand, CommandError
 from django.core.handlers.wsgi import WSGIHandler
 from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException
 from django.utils import autoreload
 
+naiveip_re = r'^(?:(?P<addr>\d{1,3}(?:\.\d{1,3}){3}|\[[a-fA-F0-9:]+\]):)?(?P<port>\d+)$'
+DEFAULT_PORT = "8000"
+
 class BaseRunserverCommand(BaseCommand):
     option_list = BaseCommand.option_list + (
+        make_option('--ipv6', '-6', action='store_true', dest='use_ipv6', default=False,
+            help='Tells Django to use a IPv6 address.'),
         make_option('--noreload', action='store_false', dest='use_reloader', default=True,
             help='Tells Django to NOT use the auto-reloader.'),
     )
@@ -25,22 +32,31 @@ class BaseRunserverCommand(BaseCommand):
         return WSGIHandler()
 
     def handle(self, addrport='', *args, **options):
+        self.use_ipv6 = options.get('use_ipv6')
+        if self.use_ipv6 and not hasattr(socket, 'AF_INET6'):
+            raise CommandError('Your Python does not support IPv6.')
         if args:
             raise CommandError('Usage is runserver %s' % self.args)
         if not addrport:
             self.addr = ''
-            self.port = '8000'
+            self.port = DEFAULT_PORT
         else:
-            try:
-                self.addr, self.port = addrport.split(':')
-            except ValueError:
-                self.addr, self.port = '', addrport
+            m = re.match(naiveip_re, addrport)
+            if m is None:
+                raise CommandError('%r is not a valid port number'
+                                   'or address:port pair.' % addrport)
+            self.addr, self.port = m.groups()
+            if not self.port.isdigit():
+                raise CommandError("%r is not a valid port number." % self.port)
+            if self.addr:
+                if self.addr.startswith('[') and self.addr.endswith(']'):
+                    self.addr = self.addr[1:-1]
+                    self.use_ipv6 = True
+                elif self.use_ipv6:
+                    raise CommandError('IPv6 addresses must be surrounded '
+                                       'with brackets, e.g. [::1].')
         if not self.addr:
-            self.addr = '127.0.0.1'
-
-        if not self.port.isdigit():
-            raise CommandError("%r is not a valid port number." % self.port)
-
+            self.addr = self.use_ipv6 and '::1' or '127.0.0.1'
         self.run(*args, **options)
 
     def run(self, *args, **options):
@@ -70,7 +86,7 @@ class BaseRunserverCommand(BaseCommand):
         ) % {
             "version": self.get_version(),
             "settings": settings.SETTINGS_MODULE,
-            "addr": self.addr,
+            "addr": self.use_ipv6 and '[%s]' % self.addr or self.addr,
             "port": self.port,
             "quit_command": quit_command,
         })
@@ -81,7 +97,7 @@ class BaseRunserverCommand(BaseCommand):
 
         try:
             handler = self.get_handler(*args, **options)
-            run(self.addr, int(self.port), handler)
+            run(self.addr, int(self.port), handler, ipv6=self.use_ipv6)
         except WSGIServerException, e:
             # Use helpful error messages instead of ugly tracebacks.
             ERRORS = {

+ 3 - 1
django/core/management/commands/testserver.py

@@ -9,6 +9,8 @@ class Command(BaseCommand):
         make_option('--addrport', action='store', dest='addrport',
             type='string', default='',
             help='port number or ipaddr:port to run the server on'),
+        make_option('--ipv6', '-6', action='store_true', dest='use_ipv6', default=False,
+            help='Tells Django to use a IPv6 address.'),
     )
     help = 'Runs a development server with data from the given fixture(s).'
     args = '[fixture ...]'
@@ -33,4 +35,4 @@ class Command(BaseCommand):
         # a strange error -- it causes this handle() method to be called
         # multiple times.
         shutdown_message = '\nServer stopped.\nNote that the test database, %r, has not been deleted. You can explore it on your own.' % db_name
-        call_command('runserver', addrport=addrport, shutdown_message=shutdown_message, use_reloader=False)
+        call_command('runserver', addrport=addrport, shutdown_message=shutdown_message, use_reloader=False, use_ipv6=options['use_ipv6'])

+ 8 - 3
django/core/servers/basehttp.py

@@ -10,6 +10,7 @@ been reviewed for security issues. Don't use it for production use.
 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
 import os
 import re
+import socket
 import sys
 import urllib
 import warnings
@@ -526,6 +527,11 @@ class WSGIServer(HTTPServer):
     """BaseHTTPServer that implements the Python WSGI protocol"""
     application = None
 
+    def __init__(self, *args, **kwargs):
+        if kwargs.pop('ipv6', False):
+            self.address_family = socket.AF_INET6
+        HTTPServer.__init__(self, *args, **kwargs)
+
     def server_bind(self):
         """Override server_bind to store the server name."""
         try:
@@ -683,9 +689,8 @@ class AdminMediaHandler(handlers.StaticFilesHandler):
         """
         return path.startswith(self.base_url[2]) and not self.base_url[1]
 
-
-def run(addr, port, wsgi_handler):
+def run(addr, port, wsgi_handler, ipv6=False):
     server_address = (addr, port)
-    httpd = WSGIServer(server_address, WSGIRequestHandler)
+    httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)
     httpd.set_app(wsgi_handler)
     httpd.serve_forever()

+ 4 - 1
docs/man/django-admin.1

@@ -75,7 +75,7 @@ Runs this project as a FastCGI application. Requires flup. Use
 .B runfcgi help
 for help on the KEY=val pairs.
 .TP
-.BI "runserver [" "\-\-noreload" "] [" "\-\-nostatic" "] [" "\-\-insecure" "] [" "\-\-adminmedia=ADMIN_MEDIA_PATH" "] [" "port|ipaddr:port" "]"
+.BI "runserver [" "\-\-noreload" "] [" "\-\-nostatic" "] [" "\-\-insecure" "] [" "\-\-ipv6" "] [" "\-\-adminmedia=ADMIN_MEDIA_PATH" "] [" "port|ipaddr:port" "]"
 Starts a lightweight Web server for development.
 .TP
 .BI "shell [" "\-\-plain" "]"
@@ -170,6 +170,9 @@ Disable automatic serving of static files from STATIC_URL.
 .I \-\-insecure
 Enables serving of static files even if DEBUG is False.
 .TP
+.I \-\-ipv6
+Enables IPv6 addresses.
+.TP
 .I \-\-verbosity=VERBOSITY
 Verbosity level: 0=minimal output, 1=normal output, 2=all output.
 .TP

+ 39 - 10
docs/ref/django-admin.txt

@@ -630,7 +630,7 @@ runserver [port or ipaddr:port]
 .. django-admin:: runserver
 
 Starts a lightweight development Web server on the local machine. By default,
-the server runs on port 8000 on the IP address 127.0.0.1. You can pass in an
+the server runs on port 8000 on the IP address ``127.0.0.1``. You can pass in an
 IP address and port number explicitly.
 
 If you run this script as a user with normal privileges (recommended), you
@@ -654,10 +654,15 @@ them to standard output, but it won't stop the server.
 You can run as many servers as you want, as long as they're on separate ports.
 Just execute ``django-admin.py runserver`` more than once.
 
-Note that the default IP address, 127.0.0.1, is not accessible from other
+Note that the default IP address, ``127.0.0.1``, is not accessible from other
 machines on your network. To make your development server viewable to other
 machines on the network, use its own IP address (e.g. ``192.168.2.1``) or
-``0.0.0.0``.
+``0.0.0.0`` or ``::`` (with IPv6 enabled).
+
+.. versionchanged:: 1.3
+
+You can also provide an IPv6 address surrounded by brackets
+(eg. ``[200a::1]:8000``). This will automaticaly enable IPv6 support.
 
 .. django-admin-option:: --adminmedia
 
@@ -681,25 +686,49 @@ Example usage::
 
     django-admin.py runserver --noreload
 
+.. django-admin-option:: --ipv6, -6
+
+.. versionadded:: 1.3
+
+Use the ``--ipv6`` (or shorter ``-6``) option to tell Django to use IPv6 for
+the development server. This changes the default IP address from
+``127.0.0.1`` to ``::1``.
+
+Example usage::
+
+    django-admin.py runserver --ipv6
+
 Examples of using different ports and addresses
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Port 8000 on IP address 127.0.0.1::
+Port 8000 on IP address ``127.0.0.1``::
 
-	django-admin.py runserver
+    django-admin.py runserver
 
-Port 8000 on IP address 1.2.3.4::
+Port 8000 on IP address ``1.2.3.4``::
 
-	django-admin.py runserver 1.2.3.4:8000
+    django-admin.py runserver 1.2.3.4:8000
 
-Port 7000 on IP address 127.0.0.1::
+Port 7000 on IP address ``127.0.0.1``::
 
     django-admin.py runserver 7000
 
-Port 7000 on IP address 1.2.3.4::
+Port 7000 on IP address ``1.2.3.4``::
 
     django-admin.py runserver 1.2.3.4:7000
 
+Port 8000 on IPv6 address ``::1``::
+
+    django-admin.py runserver -6
+
+Port 7000 on IPv6 address ``::1``::
+
+    django-admin.py runserver -6 7000
+
+Port 7000 on IPv6 address ``2001:0db8:1234:5678::9``::
+
+    django-admin.py runserver [2001:0db8:1234:5678::9]:7000
+
 Serving static files with the development server
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -963,7 +992,7 @@ templates.
 .. django-admin-option:: --addrport [port number or ipaddr:port]
 
 Use ``--addrport`` to specify a different port, or IP address and port, from
-the default of 127.0.0.1:8000. This value follows exactly the same format and
+the default of ``127.0.0.1:8000``. This value follows exactly the same format and
 serves exactly the same function as the argument to the ``runserver`` command.
 
 Examples: