Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #14928 -- Ensure that a fully qualified domain name can be used…

… 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...
commit d7fa33af78aeae8a6a6e8261c697c7a03a821595 1 parent df5c01b
Russell Keith-Magee authored January 15, 2011
24  django/core/management/commands/runserver.py
@@ -9,7 +9,12 @@
9 9
 from django.core.servers.basehttp import AdminMediaHandler, run, WSGIServerException
10 10
 from django.utils import autoreload
11 11
 
12  
-naiveip_re = r'^(?:(?P<addr>\d{1,3}(?:\.\d{1,3}){3}|\[[a-fA-F0-9:]+\]):)?(?P<port>\d+)$'
  12
+naiveip_re = re.compile(r"""^(?:
  13
+(?P<addr>
  14
+    (?P<ipv4>\d{1,3}(?:\.\d{1,3}){3}) |         # IPv4 address
  15
+    (?P<ipv6>\[[a-fA-F0-9:]+\]) |               # IPv6 address
  16
+    (?P<fqdn>[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*) # FQDN
  17
+):)?(?P<port>\d+)$""", re.X)
13 18
 DEFAULT_PORT = "8000"
14 19
 
15 20
 class BaseRunserverCommand(BaseCommand):
@@ -33,28 +38,29 @@ def get_handler(self, *args, **options):
33 38
 
34 39
     def handle(self, addrport='', *args, **options):
35 40
         self.use_ipv6 = options.get('use_ipv6')
36  
-        if self.use_ipv6 and not hasattr(socket, 'AF_INET6'):
  41
+        if self.use_ipv6 and not socket.has_ipv6:
37 42
             raise CommandError('Your Python does not support IPv6.')
38 43
         if args:
39 44
             raise CommandError('Usage is runserver %s' % self.args)
  45
+        self._raw_ipv6 = False
40 46
         if not addrport:
41 47
             self.addr = ''
42 48
             self.port = DEFAULT_PORT
43 49
         else:
44 50
             m = re.match(naiveip_re, addrport)
45 51
             if m is None:
46  
-                raise CommandError('%r is not a valid port number'
  52
+                raise CommandError('"%s" is not a valid port number '
47 53
                                    'or address:port pair.' % addrport)
48  
-            self.addr, self.port = m.groups()
  54
+            self.addr, _ipv4, _ipv6, _fqdn, self.port = m.groups()
49 55
             if not self.port.isdigit():
50 56
                 raise CommandError("%r is not a valid port number." % self.port)
51 57
             if self.addr:
52  
-                if self.addr.startswith('[') and self.addr.endswith(']'):
  58
+                if _ipv6:
53 59
                     self.addr = self.addr[1:-1]
54 60
                     self.use_ipv6 = True
55  
-                elif self.use_ipv6:
56  
-                    raise CommandError('IPv6 addresses must be surrounded '
57  
-                                       'with brackets, e.g. [::1].')
  61
+                    self._raw_ipv6 = True
  62
+                elif self.use_ipv6 and not _fqdn:
  63
+                    raise CommandError('"%s" is not a valid IPv6 address.' % self.addr)
58 64
         if not self.addr:
59 65
             self.addr = self.use_ipv6 and '::1' or '127.0.0.1'
60 66
         self.run(*args, **options)
@@ -86,7 +92,7 @@ def inner_run(self, *args, **options):
86 92
         ) % {
87 93
             "version": self.get_version(),
88 94
             "settings": settings.SETTINGS_MODULE,
89  
-            "addr": self.use_ipv6 and '[%s]' % self.addr or self.addr,
  95
+            "addr": self._raw_ipv6 and '[%s]' % self.addr or self.addr,
90 96
             "port": self.port,
91 97
             "quit_command": quit_command,
92 98
         })
16  docs/ref/django-admin.txt
@@ -616,7 +616,7 @@ Example usage::
616 616
 
617 617
 Run a FastCGI server as a daemon and write the spawned PID in a file.
618 618
 
619  
-runserver [port or ipaddr:port]
  619
+runserver [port or address:port]
620 620
 -------------------------------
621 621
 
622 622
 .. django-admin:: runserver
@@ -653,8 +653,10 @@ machines on the network, use its own IP address (e.g. ``192.168.2.1``) or
653 653
 
654 654
 .. versionchanged:: 1.3
655 655
 
656  
-You can also provide an IPv6 address surrounded by brackets
657  
-(eg. ``[200a::1]:8000``). This will automaticaly enable IPv6 support.
  656
+You can provide an IPv6 address surrounded by brackets
  657
+(e.g. ``[200a::1]:8000``). This will automatically enable IPv6 support.
  658
+
  659
+A hostname containing ASCII-only characters can also be used.
658 660
 
659 661
 .. django-admin-option:: --adminmedia
660 662
 
@@ -721,6 +723,14 @@ Port 7000 on IPv6 address ``2001:0db8:1234:5678::9``::
721 723
 
722 724
     django-admin.py runserver [2001:0db8:1234:5678::9]:7000
723 725
 
  726
+Port 8000 on IPv4 address of host ``localhost``::
  727
+
  728
+    django-admin.py runserver localhost:8000
  729
+
  730
+Port 8000 on IPv6 address of host ``localhost``::
  731
+
  732
+    django-admin.py runserver -6 localhost:8000
  733
+
724 734
 Serving static files with the development server
725 735
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
726 736
 
55  tests/regressiontests/admin_scripts/tests.py
@@ -999,6 +999,61 @@ def test_app_with_import(self):
999 999
         self.assertNoOutput(err)
1000 1000
         self.assertOutput(out, '0 errors found')
1001 1001
 
  1002
+class ManageRunserver(AdminScriptTestCase):
  1003
+    def setUp(self):
  1004
+        from django.core.management.commands.runserver import BaseRunserverCommand
  1005
+        def monkey_run(*args, **options): return
  1006
+
  1007
+        self.cmd = BaseRunserverCommand()
  1008
+        self.cmd.run = monkey_run
  1009
+
  1010
+    def assertServerSettings(self, addr, port, ipv6=None, raw_ipv6=False):
  1011
+        self.assertEqual(self.cmd.addr, addr)
  1012
+        self.assertEqual(self.cmd.port, port)
  1013
+        self.assertEqual(self.cmd.use_ipv6, ipv6)
  1014
+        self.assertEqual(self.cmd._raw_ipv6, raw_ipv6)
  1015
+
  1016
+    def test_runserver_addrport(self):
  1017
+        self.cmd.handle()
  1018
+        self.assertServerSettings('127.0.0.1', '8000')
  1019
+
  1020
+        self.cmd.handle(addrport="1.2.3.4:8000")
  1021
+        self.assertServerSettings('1.2.3.4', '8000')
  1022
+
  1023
+        self.cmd.handle(addrport="7000")
  1024
+        self.assertServerSettings('127.0.0.1', '7000')
  1025
+
  1026
+        # IPv6
  1027
+        self.cmd.handle(addrport="", use_ipv6=True)
  1028
+        self.assertServerSettings('::1', '8000', ipv6=True)
  1029
+
  1030
+        self.cmd.handle(addrport="7000", use_ipv6=True)
  1031
+        self.assertServerSettings('::1', '7000', ipv6=True)
  1032
+
  1033
+        self.cmd.handle(addrport="[2001:0db8:1234:5678::9]:7000")
  1034
+        self.assertServerSettings('2001:0db8:1234:5678::9', '7000', ipv6=True, raw_ipv6=True)
  1035
+
  1036
+        # Hostname
  1037
+        self.cmd.handle(addrport="localhost:8000")
  1038
+        self.assertServerSettings('localhost', '8000')
  1039
+
  1040
+        self.cmd.handle(addrport="test.domain.local:7000")
  1041
+        self.assertServerSettings('test.domain.local', '7000')
  1042
+
  1043
+        self.cmd.handle(addrport="test.domain.local:7000", use_ipv6=True)
  1044
+        self.assertServerSettings('test.domain.local', '7000', ipv6=True)
  1045
+
  1046
+        # Potentially ambiguous
  1047
+
  1048
+        # Only 4 characters, all of which coudl be in an ipv6 address
  1049
+        self.cmd.handle(addrport="beef:7654")
  1050
+        self.assertServerSettings('beef', '7654')
  1051
+
  1052
+        # Uses only characters that could be in an ipv6 address
  1053
+        self.cmd.handle(addrport="deadbeef:7654")
  1054
+        self.assertServerSettings('deadbeef', '7654')
  1055
+
  1056
+
1002 1057
 ##########################################################################
1003 1058
 # COMMAND PROCESSING TESTS
1004 1059
 # Check that user-space commands are correctly handled - in particular,

0 notes on commit d7fa33a

Please sign in to comment.
Something went wrong with that request. Please try again.