- MAX_CHILDREN = 20
-
- def __init__(self, mainloop, local_address, port,
- ssl_params=None, ssl_verify_peer=False):
- """Initializes the HTTP server
-
- @type mainloop: ganeti.daemon.Mainloop
- @param mainloop: Mainloop used to poll for I/O events
- @type local_addess: string
- @param local_address: Local IP address to bind to
- @type port: int
- @param port: TCP port to listen on
- @type ssl_params: HttpSslParams
- @param ssl_params: SSL key and certificate
- @type ssl_verify_peer: bool
- @param ssl_verify_peer: Whether to require client certificate and compare
- it with our certificate
-
- """
- HttpSocketBase.__init__(self)
-
- self.mainloop = mainloop
- self.local_address = local_address
- self.port = port
-
- self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
-
- # Allow port to be reused
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-
- if self._using_ssl:
- self._fileio_class = _SSLFileObject
- else:
- self._fileio_class = socket._fileobject
-
- self._children = []
-
- mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
- mainloop.RegisterSignal(self)
-
- def Start(self):
- self.socket.bind((self.local_address, self.port))
- self.socket.listen(5)
-
- def Stop(self):
- self.socket.close()
-
- def OnIO(self, fd, condition):
- if condition & select.POLLIN:
- self._IncomingConnection()
-
- def OnSignal(self, signum):
- if signum == signal.SIGCHLD:
- self._CollectChildren(True)
-
- def _CollectChildren(self, quick):
- """Checks whether any child processes are done
-
- @type quick: bool
- @param quick: Whether to only use non-blocking functions
-
- """
- if not quick:
- # Don't wait for other processes if it should be a quick check
- while len(self._children) > self.MAX_CHILDREN:
- try:
- # Waiting without a timeout brings us into a potential DoS situation.
- # As soon as too many children run, we'll not respond to new
- # requests. The real solution would be to add a timeout for children
- # and killing them after some time.
- pid, status = os.waitpid(0, 0)
- except os.error:
- pid = None
- if pid and pid in self._children:
- self._children.remove(pid)
-
- for child in self._children:
- try:
- pid, status = os.waitpid(child, os.WNOHANG)
- except os.error:
- pid = None
- if pid and pid in self._children:
- self._children.remove(pid)
-
- def _IncomingConnection(self):
- """Called for each incoming connection
-
- """
- (connection, client_addr) = self.socket.accept()
-
- self._CollectChildren(False)
-
- pid = os.fork()
- if pid == 0:
- # Child process
- try:
- HttpServerRequestExecutor(self, connection, client_addr,
- self._fileio_class)
- except:
- logging.exception("Error while handling request from %s:%s",
- client_addr[0], client_addr[1])
- os._exit(1)
- os._exit(0)
- else:
- self._children.append(pid)
-
- def HandleRequest(self, req):
- raise NotImplementedError()
-
-
-class HttpClientRequest(object):
- def __init__(self, host, port, method, path, headers=None, post_data=None,
- ssl_params=None, ssl_verify_peer=False):
- """Describes an HTTP request.
-
- @type host: string
- @param host: Hostname
- @type port: int
- @param port: Port
- @type method: string
- @param method: Method name
- @type path: string
- @param path: Request path
- @type headers: dict or None
- @param headers: Additional headers to send
- @type post_data: string or None
- @param post_data: Additional data to send
- @type ssl_params: HttpSslParams
- @param ssl_params: SSL key and certificate
- @type ssl_verify_peer: bool
- @param ssl_verify_peer: Whether to compare our certificate with server's
- certificate
-
- """
- if post_data is not None:
- assert method.upper() in (HTTP_POST, HTTP_PUT), \
- "Only POST and GET requests support sending data"
-
- assert path.startswith("/"), "Path must start with slash (/)"
-
- self.host = host
- self.port = port
- self.ssl_params = ssl_params
- self.ssl_verify_peer = ssl_verify_peer
- self.method = method
- self.path = path
- self.headers = headers
- self.post_data = post_data
-
- self.success = None
- self.error = None
-
- self.resp_status_line = None
- self.resp_version = None
- self.resp_status = None
- self.resp_reason = None
- self.resp_headers = None
- self.resp_body = None
-
-
-class HttpClientRequestExecutor(HttpSocketBase):
- # Default headers
- DEFAULT_HEADERS = {
- HTTP_USER_AGENT: HTTP_GANETI_VERSION,
- # TODO: For keep-alive, don't send "Connection: close"
- HTTP_CONNECTION: "close",
- }
-