Permalink
Browse files

Pull socket creation out of HTTPServer into a new module.

  • Loading branch information...
1 parent e5136bd commit 767d83d5c17233c01527617d66409aec2f3173bc @bdarnell bdarnell committed Jul 4, 2011
Showing with 91 additions and 31 deletions.
  1. +4 −31 tornado/httpserver.py
  2. +80 −0 tornado/netutil.py
  3. +1 −0 tornado/test/import_test.py
  4. +5 −0 website/sphinx/netutil.rst
  5. +1 −0 website/sphinx/networking.rst
View
@@ -35,6 +35,7 @@ class except to start a server at the beginning of the process
from tornado import httputil
from tornado import ioloop
from tornado import iostream
+from tornado import netutil
from tornado import stack_context
from tornado.util import b, bytes_type
@@ -184,37 +185,9 @@ def bind(self, port, address=None, family=socket.AF_UNSPEC, backlog=128):
This method may be called multiple times prior to start() to listen
on multiple ports or interfaces.
"""
- if address == "":
- address = None
- flags = socket.AI_PASSIVE
- if hasattr(socket, "AI_ADDRCONFIG"):
- # AI_ADDRCONFIG ensures that we only try to bind on ipv6
- # if the system is configured for it, but the flag doesn't
- # exist on some platforms (specifically WinXP, although
- # newer versions of windows have it)
- flags |= socket.AI_ADDRCONFIG
- for res in socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,
- 0, flags):
- af, socktype, proto, canonname, sockaddr = res
- sock = socket.socket(af, socktype, proto)
- flags = fcntl.fcntl(sock.fileno(), fcntl.F_GETFD)
- flags |= fcntl.FD_CLOEXEC
- fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, flags)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if af == socket.AF_INET6:
- # On linux, ipv6 sockets accept ipv4 too by default,
- # but this makes it impossible to bind to both
- # 0.0.0.0 in ipv4 and :: in ipv6. On other systems,
- # separate sockets *must* be used to listen for both ipv4
- # and ipv6. For consistency, always disable ipv4 on our
- # ipv6 sockets and use a separate ipv4 socket when needed.
- #
- # Python 2.x on windows doesn't have IPPROTO_IPV6.
- if hasattr(socket, "IPPROTO_IPV6"):
- sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
- sock.setblocking(0)
- sock.bind(sockaddr)
- sock.listen(backlog)
+ sockets = netutil.bind_sockets(port, address=address,
+ family=family, backlog=backlog)
+ for sock in sockets:
self._sockets[sock.fileno()] = sock
if self._started:
self.io_loop.add_handler(sock.fileno(), self._handle_events,
View
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+#
+# Copyright 2011 Facebook
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Miscellaneous network utility code."""
+
+import os
+import socket
+
+try:
+ import fcntl
+except ImportError:
+ if os.name == 'nt':
+ from tornado import win32_support as fcntl
+ else:
+ raise
+
+def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128):
+ """Creates listening sockets bound to the given port and address.
+
+ Returns a list of socket objects (multiple sockets are returned if
+ the given address maps to multiple IP addresses, which is most common
+ for mixed IPv4 and IPv6 use).
+
+ Address may be either an IP address or hostname. If it's a hostname,
+ the server will listen on all IP addresses associated with the
+ name. Address may be an empty string or None to listen on all
+ available interfaces. Family may be set to either socket.AF_INET
+ or socket.AF_INET6 to restrict to ipv4 or ipv6 addresses, otherwise
+ both will be used if available.
+
+ The ``backlog`` argument has the same meaning as for
+ ``socket.listen()``.
+ """
+ sockets = []
+ if address == "":
+ address = None
+ flags = socket.AI_PASSIVE
+ if hasattr(socket, "AI_ADDRCONFIG"):
+ # AI_ADDRCONFIG ensures that we only try to bind on ipv6
+ # if the system is configured for it, but the flag doesn't
+ # exist on some platforms (specifically WinXP, although
+ # newer versions of windows have it)
+ flags |= socket.AI_ADDRCONFIG
+ for res in socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,
+ 0, flags):
+ af, socktype, proto, canonname, sockaddr = res
+ sock = socket.socket(af, socktype, proto)
+ flags = fcntl.fcntl(sock.fileno(), fcntl.F_GETFD)
+ flags |= fcntl.FD_CLOEXEC
+ fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, flags)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ if af == socket.AF_INET6:
+ # On linux, ipv6 sockets accept ipv4 too by default,
+ # but this makes it impossible to bind to both
+ # 0.0.0.0 in ipv4 and :: in ipv6. On other systems,
+ # separate sockets *must* be used to listen for both ipv4
+ # and ipv6. For consistency, always disable ipv4 on our
+ # ipv6 sockets and use a separate ipv4 socket when needed.
+ #
+ # Python 2.x on windows doesn't have IPPROTO_IPV6.
+ if hasattr(socket, "IPPROTO_IPV6"):
+ sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
+ sock.setblocking(0)
+ sock.bind(sockaddr)
+ sock.listen(backlog)
+ sockets.append(sock)
+ return sockets
@@ -17,6 +17,7 @@ def test_import_everything(self):
import tornado.iostream
import tornado.locale
import tornado.options
+ import tornado.netutil
import tornado.simple_httpclient
import tornado.stack_context
import tornado.template
@@ -0,0 +1,5 @@
+``tornado.netutil`` --- Miscellaneous network utilities
+=======================================================
+
+.. automodule:: tornado.netutil
+ :members:
@@ -6,3 +6,4 @@ Asynchronous networking
ioloop
iostream
httpclient
+ netutil

0 comments on commit 767d83d

Please sign in to comment.