Skip to content

Commit

Permalink
Fixed #14928 -- Ensure that a fully qualified domain name can be used…
Browse files Browse the repository at this point in the history
… for runserver. Thanks to Karmel Allison for the report, Łukasz Rekucki for the patch, and claudep for the tests.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15215 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
freakboy3742 committed Jan 15, 2011
1 parent df5c01b commit d7fa33a
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 12 deletions.
24 changes: 15 additions & 9 deletions django/core/management/commands/runserver.py
Expand Up @@ -9,7 +9,12 @@
from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException
from django.utils import autoreload from django.utils import autoreload


naiveip_re = r'^(?:(?P<addr>\d{1,3}(?:\.\d{1,3}){3}|\[[a-fA-F0-9:]+\]):)?(?P<port>\d+)$' naiveip_re = re.compile(r"""^(?:
(?P<addr>
(?P<ipv4>\d{1,3}(?:\.\d{1,3}){3}) | # IPv4 address
(?P<ipv6>\[[a-fA-F0-9:]+\]) | # IPv6 address
(?P<fqdn>[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*) # FQDN
):)?(?P<port>\d+)$""", re.X)
DEFAULT_PORT = "8000" DEFAULT_PORT = "8000"


class BaseRunserverCommand(BaseCommand): class BaseRunserverCommand(BaseCommand):
Expand All @@ -33,28 +38,29 @@ def get_handler(self, *args, **options):


def handle(self, addrport='', *args, **options): def handle(self, addrport='', *args, **options):
self.use_ipv6 = options.get('use_ipv6') self.use_ipv6 = options.get('use_ipv6')
if self.use_ipv6 and not hasattr(socket, 'AF_INET6'): if self.use_ipv6 and not socket.has_ipv6:
raise CommandError('Your Python does not support IPv6.') raise CommandError('Your Python does not support IPv6.')
if args: if args:
raise CommandError('Usage is runserver %s' % self.args) raise CommandError('Usage is runserver %s' % self.args)
self._raw_ipv6 = False
if not addrport: if not addrport:
self.addr = '' self.addr = ''
self.port = DEFAULT_PORT self.port = DEFAULT_PORT
else: else:
m = re.match(naiveip_re, addrport) m = re.match(naiveip_re, addrport)
if m is None: if m is None:
raise CommandError('%r is not a valid port number' raise CommandError('"%s" is not a valid port number '
'or address:port pair.' % addrport) 'or address:port pair.' % addrport)
self.addr, self.port = m.groups() self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups()
if not self.port.isdigit(): if not self.port.isdigit():
raise CommandError("%r is not a valid port number." % self.port) raise CommandError("%r is not a valid port number." % self.port)
if self.addr: if self.addr:
if self.addr.startswith('[') and self.addr.endswith(']'): if _ipv6:
self.addr = self.addr[1:-1] self.addr = self.addr[1:-1]
self.use_ipv6 = True self.use_ipv6 = True
elif self.use_ipv6: self._raw_ipv6 = True
raise CommandError('IPv6 addresses must be surrounded ' elif self.use_ipv6 and not _fqdn:
'with brackets, e.g. [::1].') raise CommandError('"%s" is not a valid IPv6 address.' % self.addr)
if not self.addr: if not self.addr:
self.addr = self.use_ipv6 and '::1' or '127.0.0.1' self.addr = self.use_ipv6 and '::1' or '127.0.0.1'
self.run(*args, **options) self.run(*args, **options)
Expand Down Expand Up @@ -86,7 +92,7 @@ def inner_run(self, *args, **options):
) % { ) % {
"version": self.get_version(), "version": self.get_version(),
"settings": settings.SETTINGS_MODULE, "settings": settings.SETTINGS_MODULE,
"addr": self.use_ipv6 and '[%s]' % self.addr or self.addr, "addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,
"port": self.port, "port": self.port,
"quit_command": quit_command, "quit_command": quit_command,
}) })
Expand Down
16 changes: 13 additions & 3 deletions docs/ref/django-admin.txt
Expand Up @@ -616,7 +616,7 @@ Example usage::


Run a FastCGI server as a daemon and write the spawned PID in a file. Run a FastCGI server as a daemon and write the spawned PID in a file.


runserver [port or ipaddr:port] runserver [port or address:port]
------------------------------- -------------------------------


.. django-admin:: runserver .. django-admin:: runserver
Expand Down Expand Up @@ -653,8 +653,10 @@ machines on the network, use its own IP address (e.g. ``192.168.2.1``) or


.. versionchanged:: 1.3 .. versionchanged:: 1.3


You can also provide an IPv6 address surrounded by brackets You can provide an IPv6 address surrounded by brackets
(eg. ``[200a::1]:8000``). This will automaticaly enable IPv6 support. (e.g. ``[200a::1]:8000``). This will automatically enable IPv6 support.

A hostname containing ASCII-only characters can also be used.


.. django-admin-option:: --adminmedia .. django-admin-option:: --adminmedia


Expand Down Expand Up @@ -721,6 +723,14 @@ Port 7000 on IPv6 address ``2001:0db8:1234:5678::9``::


django-admin.py runserver [2001:0db8:1234:5678::9]:7000 django-admin.py runserver [2001:0db8:1234:5678::9]:7000


Port 8000 on IPv4 address of host ``localhost``::

django-admin.py runserver localhost:8000

Port 8000 on IPv6 address of host ``localhost``::

django-admin.py runserver -6 localhost:8000

Serving static files with the development server Serving static files with the development server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Expand Down
55 changes: 55 additions & 0 deletions tests/regressiontests/admin_scripts/tests.py
Expand Up @@ -999,6 +999,61 @@ def test_app_with_import(self):
self.assertNoOutput(err) self.assertNoOutput(err)
self.assertOutput(out, '0 errors found') self.assertOutput(out, '0 errors found')


class ManageRunserver(AdminScriptTestCase):
def setUp(self):
from django.core.management.commands.runserver import BaseRunserverCommand
def monkey_run(*args, **options): return

self.cmd = BaseRunserverCommand()
self.cmd.run = monkey_run

def assertServerSettings(self, addr, port, ipv6=None, raw_ipv6=False):
self.assertEqual(self.cmd.addr, addr)
self.assertEqual(self.cmd.port, port)
self.assertEqual(self.cmd.use_ipv6, ipv6)
self.assertEqual(self.cmd._raw_ipv6, raw_ipv6)

def test_runserver_addrport(self):
self.cmd.handle()
self.assertServerSettings('127.0.0.1', '8000')

self.cmd.handle(addrport="1.2.3.4:8000")
self.assertServerSettings('1.2.3.4', '8000')

self.cmd.handle(addrport="7000")
self.assertServerSettings('127.0.0.1', '7000')

# IPv6
self.cmd.handle(addrport="", use_ipv6=True)
self.assertServerSettings('::1', '8000', ipv6=True)

self.cmd.handle(addrport="7000", use_ipv6=True)
self.assertServerSettings('::1', '7000', ipv6=True)

self.cmd.handle(addrport="[2001:0db8:1234:5678::9]:7000")
self.assertServerSettings('2001:0db8:1234:5678::9', '7000', ipv6=True, raw_ipv6=True)

# Hostname
self.cmd.handle(addrport="localhost:8000")
self.assertServerSettings('localhost', '8000')

self.cmd.handle(addrport="test.domain.local:7000")
self.assertServerSettings('test.domain.local', '7000')

self.cmd.handle(addrport="test.domain.local:7000", use_ipv6=True)
self.assertServerSettings('test.domain.local', '7000', ipv6=True)

# Potentially ambiguous

# Only 4 characters, all of which coudl be in an ipv6 address
self.cmd.handle(addrport="beef:7654")
self.assertServerSettings('beef', '7654')

# Uses only characters that could be in an ipv6 address
self.cmd.handle(addrport="deadbeef:7654")
self.assertServerSettings('deadbeef', '7654')


########################################################################## ##########################################################################
# COMMAND PROCESSING TESTS # COMMAND PROCESSING TESTS
# Check that user-space commands are correctly handled - in particular, # Check that user-space commands are correctly handled - in particular,
Expand Down

0 comments on commit d7fa33a

Please sign in to comment.