For simple_server.py, inherit listening socket if present.
This adds support for getting the listening socket from systemd.
This commit is contained in:
parent
74b3b3750f
commit
0e9b73be39
|
@ -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:
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue