From 6a32e253f68b62b75d787f698ebaef461886c21c Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Fri, 26 Nov 2010 13:33:53 +0000 Subject: [PATCH] =?UTF-8?q?Fixed=20#7735=20--=20Added=20support=20for=20IP?= =?UTF-8?q?v6=20adresses=20to=20runserver=20and=20testserver=20management?= =?UTF-8?q?=20command.=20Thanks=20to=20Jason=20Alonso=20and=20=C5=81ukasz?= =?UTF-8?q?=20Rekucki=20for=20the=20report=20and=20initial=20patches.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://code.djangoproject.com/svn/django/trunk@14711 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/management/commands/runserver.py | 40 ++++++++++----- django/core/management/commands/testserver.py | 4 +- django/core/servers/basehttp.py | 11 +++-- docs/man/django-admin.1 | 5 +- docs/ref/django-admin.txt | 49 +++++++++++++++---- 5 files changed, 82 insertions(+), 27 deletions(-) diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index 892250fdba..b63b57a67c 100644 --- a/django/core/management/commands/runserver.py +++ b/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\d{1,3}(?:\.\d{1,3}){3}|\[[a-fA-F0-9:]+\]):)?(?P\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 = { diff --git a/django/core/management/commands/testserver.py b/django/core/management/commands/testserver.py index d3d9698c9a..3f62e5775b 100644 --- a/django/core/management/commands/testserver.py +++ b/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']) diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index 96bb178547..7772a0b5f8 100644 --- a/django/core/servers/basehttp.py +++ b/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() diff --git a/docs/man/django-admin.1 b/docs/man/django-admin.1 index f9b530a77e..73815ef68b 100644 --- a/docs/man/django-admin.1 +++ b/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 diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index ea49e2372d..eeb5fee911 100644 --- a/docs/ref/django-admin.txt +++ b/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: