mirror of https://github.com/django/django.git
[1.5.x] Fixed "Address already in use" from liveserver.
Our WSGIServer rewrapped the socket errors from server_bind into
WSGIServerExceptions, which is used later on to provide nicer
error messages in runserver and used by the liveserver to see if
the port is already in use. But wrapping server_bind isn't enough since
it only binds to the socket, socket.listen (which is called from
server_activate) could also raise "Address already in use".
Instead of overriding server_activate too I chose to just catch socket
errors, which seems to make more sense anyways and should be more robust
against changes in wsgiref.
Backport of 2ca00faa91
from master
This commit is contained in:
parent
b5eddde095
commit
18fe77e4ed
|
@ -1,12 +1,13 @@
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import errno
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.core.servers.basehttp import run, WSGIServerException, get_internal_wsgi_application
|
from django.core.servers.basehttp import run, get_internal_wsgi_application
|
||||||
from django.utils import autoreload
|
from django.utils import autoreload
|
||||||
|
|
||||||
naiveip_re = re.compile(r"""^(?:
|
naiveip_re = re.compile(r"""^(?:
|
||||||
|
@ -112,16 +113,16 @@ class Command(BaseCommand):
|
||||||
handler = self.get_handler(*args, **options)
|
handler = self.get_handler(*args, **options)
|
||||||
run(self.addr, int(self.port), handler,
|
run(self.addr, int(self.port), handler,
|
||||||
ipv6=self.use_ipv6, threading=threading)
|
ipv6=self.use_ipv6, threading=threading)
|
||||||
except WSGIServerException as e:
|
except socket.error as e:
|
||||||
# Use helpful error messages instead of ugly tracebacks.
|
# Use helpful error messages instead of ugly tracebacks.
|
||||||
ERRORS = {
|
ERRORS = {
|
||||||
13: "You don't have permission to access that port.",
|
errno.EACCES: "You don't have permission to access that port.",
|
||||||
98: "That port is already in use.",
|
errno.EADDRINUSE: "That port is already in use.",
|
||||||
99: "That IP address can't be assigned-to.",
|
errno.EADDRNOTAVAIL: "That IP address can't be assigned-to.",
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
error_text = ERRORS[e.args[0].args[0]]
|
error_text = ERRORS[e.errno]
|
||||||
except (AttributeError, KeyError):
|
except KeyError:
|
||||||
error_text = str(e)
|
error_text = str(e)
|
||||||
self.stderr.write("Error: %s" % error_text)
|
self.stderr.write("Error: %s" % error_text)
|
||||||
# Need to use an OS exit because sys.exit doesn't work in a thread
|
# Need to use an OS exit because sys.exit doesn't work in a thread
|
||||||
|
|
|
@ -67,10 +67,6 @@ def get_internal_wsgi_application():
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
class WSGIServerException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ServerHandler(simple_server.ServerHandler, object):
|
class ServerHandler(simple_server.ServerHandler, object):
|
||||||
error_status = str("500 INTERNAL SERVER ERROR")
|
error_status = str("500 INTERNAL SERVER ERROR")
|
||||||
|
|
||||||
|
@ -131,10 +127,7 @@ class WSGIServer(simple_server.WSGIServer, object):
|
||||||
|
|
||||||
def server_bind(self):
|
def server_bind(self):
|
||||||
"""Override server_bind to store the server name."""
|
"""Override server_bind to store the server name."""
|
||||||
try:
|
super(WSGIServer, self).server_bind()
|
||||||
super(WSGIServer, self).server_bind()
|
|
||||||
except Exception as e:
|
|
||||||
raise WSGIServerException(e)
|
|
||||||
self.setup_environ()
|
self.setup_environ()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import difflib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -24,8 +25,7 @@ from django.core.handlers.wsgi import WSGIHandler
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.management.color import no_style
|
from django.core.management.color import no_style
|
||||||
from django.core.signals import request_started
|
from django.core.signals import request_started
|
||||||
from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
|
from django.core.servers.basehttp import WSGIRequestHandler, WSGIServer
|
||||||
WSGIServerException)
|
|
||||||
from django.core.urlresolvers import clear_url_caches
|
from django.core.urlresolvers import clear_url_caches
|
||||||
from django.core.validators import EMPTY_VALUES
|
from django.core.validators import EMPTY_VALUES
|
||||||
from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS,
|
from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS,
|
||||||
|
@ -1064,10 +1064,9 @@ class LiveServerThread(threading.Thread):
|
||||||
try:
|
try:
|
||||||
self.httpd = StoppableWSGIServer(
|
self.httpd = StoppableWSGIServer(
|
||||||
(self.host, port), QuietWSGIRequestHandler)
|
(self.host, port), QuietWSGIRequestHandler)
|
||||||
except WSGIServerException as e:
|
except socket.error as e:
|
||||||
if (index + 1 < len(self.possible_ports) and
|
if (index + 1 < len(self.possible_ports) and
|
||||||
hasattr(e.args[0], 'errno') and
|
e.errno == errno.EADDRINUSE):
|
||||||
e.args[0].errno == errno.EADDRINUSE):
|
|
||||||
# This port is already in use, so we go on and try with
|
# This port is already in use, so we go on and try with
|
||||||
# the next one in the list.
|
# the next one in the list.
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -5,6 +5,7 @@ Tests for django.core.servers.
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
try:
|
try:
|
||||||
from urllib.request import urlopen, HTTPError
|
from urllib.request import urlopen, HTTPError
|
||||||
except ImportError: # Python 2
|
except ImportError: # Python 2
|
||||||
|
@ -12,7 +13,6 @@ except ImportError: # Python 2
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.test import LiveServerTestCase
|
from django.test import LiveServerTestCase
|
||||||
from django.core.servers.basehttp import WSGIServerException
|
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
|
@ -66,7 +66,7 @@ class LiveServerAddress(LiveServerBase):
|
||||||
cls.raises_exception('localhost', ImproperlyConfigured)
|
cls.raises_exception('localhost', ImproperlyConfigured)
|
||||||
|
|
||||||
# The host must be valid
|
# The host must be valid
|
||||||
cls.raises_exception('blahblahblah:8081', WSGIServerException)
|
cls.raises_exception('blahblahblah:8081', socket.error)
|
||||||
|
|
||||||
# The list of ports must be in a valid format
|
# The list of ports must be in a valid format
|
||||||
cls.raises_exception('localhost:8081,', ImproperlyConfigured)
|
cls.raises_exception('localhost:8081,', ImproperlyConfigured)
|
||||||
|
|
Loading…
Reference in New Issue