For simple_server.py, inherit listening socket if present.

This adds support for getting the listening socket from
systemd.
This commit is contained in:
Neil Schemenauer 2016-06-24 18:08:22 +00:00
parent 74b3b3750f
commit 0e9b73be39
2 changed files with 104 additions and 1 deletions

View File

@ -2,11 +2,48 @@
"""A simple, single threaded, synchronous HTTP server.
"""
import sys
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import BaseServer
import urllib.request, urllib.parse, urllib.error
import quixote
from quixote import get_publisher
from quixote.util import import_object
from scgi.systemd_socket import get_systemd_socket
class SockInheritHTTPServer(HTTPServer):
def __init__(self, address_info, handler, bind_and_activate=True):
# This is ugly. We have to re-implement HTTPServer.__init__
# and server_bind(). We want to get the inherited socket if
# available. If we inherit then we need to skip the bind() call.
BaseServer.__init__(self, address_info, handler)
sock = get_systemd_socket()
if sock is not None:
print('Using inherited socket %s' % (sock.getsockname(),))
self._skip_bind = True
else:
sock = socket.socket(self.address_family, self.socket_type)
self._skip_bind = False
self.socket = sock
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
def server_bind(self):
if not self._skip_bind:
HTTPServer.server_bind(self)
else:
self.server_address = self.socket.getsockname()
host, port = self.socket.getsockname()[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port
class HTTPRequestHandler(BaseHTTPRequestHandler):
@ -83,7 +120,7 @@ def run(create_publisher, host='', port=80, https=False):
"""
if https:
HTTPRequestHandler.required_cgi_environment['HTTPS'] = 'on'
httpd = HTTPServer((host, port), HTTPRequestHandler)
httpd = SockInheritHTTPServer((host, port), HTTPRequestHandler)
def handle_error(request, client_address):
HTTPServer.handle_error(httpd, request, client_address)
if sys.exc_info()[0] is SystemExit:

View File

@ -0,0 +1,66 @@
# Inherit activation sockets from systemd, see systemd man page for
# sd_listen_fds().
import os
import socket
SD_LISTEN_FDS_START = 3
def _set_close_on_exec(fds):
try:
import fcntl
except ImportError:
return
if not hasattr(fcntl, 'FD_CLOEXEC'):
return
for fd in range(SD_LISTEN_FDS_START, SD_LISTEN_FDS_START + fds):
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
def sd_listen_fds():
"""Return the number of inherited sockets. Return zero if there are
none.
"""
try:
pid = int(os.environ['LISTEN_PID'])
except (ValueError, KeyError):
return 0
if os.getpid() != pid:
return 0
try:
fds = int(os.environ['LISTEN_FDS'])
except (ValueError, KeyError):
raise OSError('invalid LISTEN_FDS value')
_set_close_on_exec(fds)
return fds
def _socket_from_fd(fd):
# This is ugly; Python doesn't provide a nice way to use
# getsockopt() and getsockname() to determine the type of
# socket. Using AF_UNIX is a kludge to avoid messing up the
# getsockname() return value.
s = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
name = s.getsockname()
s.close() # fromfd() calls dup, close the new fd
if isinstance(name, (str, bytes)):
family = socket.AF_UNIX
elif ':' in name[0]:
family = socket.AF_INET6
else:
family = socket.AF_INET
# we assume we are getting a SOCK_STREAM socket
s = socket.fromfd(fd, family, socket.SOCK_STREAM)
os.close(fd) # fromfd() calls dup, close old fd
return s
def get_systemd_socket():
"""Return the inherited socket, if there is one. If not, return None.
"""
num = sd_listen_fds()
if not num:
return None
if num > 1:
raise OSError('only one inherited socket supported')
sock = _socket_from_fd(SD_LISTEN_FDS_START)
return sock