X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/5bbd3f7f72506933b9ab86d2d1faff85ab3b4551..dadf6b7d371ff1ee7cd3dd51dd8e633133233317:/lib/http/server.py diff --git a/lib/http/server.py b/lib/http/server.py index 95a3756..7a46af6 100644 --- a/lib/http/server.py +++ b/lib/http/server.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2007, 2008 Google Inc. +# Copyright (C) 2007, 2008, 2010 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,15 +26,14 @@ import BaseHTTPServer import cgi import logging import os -import select import socket import time import signal +import asyncore -from ganeti import constants -from ganeti import serializer -from ganeti import utils from ganeti import http +from ganeti import utils +from ganeti import netutils WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] @@ -76,12 +75,12 @@ class _HttpServerRequest(object): """Data structure for HTTP request on server side. """ - def __init__(self, request_msg): + def __init__(self, method, path, headers, body): # Request attributes - self.request_method = request_msg.start_line.method - self.request_path = request_msg.start_line.path - self.request_headers = request_msg.headers - self.request_body = request_msg.decoded_body + self.request_method = method + self.request_path = path + self.request_headers = headers + self.request_body = body # Response attributes self.resp_headers = {} @@ -90,6 +89,14 @@ class _HttpServerRequest(object): # authentication) self.private = None + def __repr__(self): + status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__), + self.request_method, self.request_path, + "headers=%r" % str(self.request_headers), + "body=%r" % (self.request_body, )] + + return "<%s at %#x>" % (" ".join(status), id(self)) + class _HttpServerToClientMessageWriter(http.HttpMessageWriter): """Writes an HTTP response to client. @@ -271,6 +278,14 @@ class HttpServerRequestExecutor(object): try: try: request_msg_reader = self._ReadRequest() + + # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond + # with a 400 (Bad Request) status code to any HTTP/1.1 request + # message which lacks a Host header field. + if (self.request_msg.start_line.version == http.HTTP_1_1 and + http.HTTP_HOST not in self.request_msg.headers): + raise http.HttpBadRequest(message="Missing Host header") + self._HandleRequest() # Only wait for client to close if we didn't have any exception. @@ -310,7 +325,12 @@ class HttpServerRequestExecutor(object): """Calls the handler function for the current request. """ - handler_context = _HttpServerRequest(self.request_msg) + handler_context = _HttpServerRequest(self.request_msg.start_line.method, + self.request_msg.start_line.path, + self.request_msg.headers, + self.request_msg.body) + + logging.debug("Handling request %r", handler_context) try: try: @@ -328,12 +348,12 @@ class HttpServerRequestExecutor(object): logging.exception("Unknown exception") raise http.HttpInternalServerError(message="Unknown error") - # TODO: Content-type - encoder = http.HttpJsonConverter() + if not isinstance(result, basestring): + raise http.HttpError("Handler function didn't return string type") + self.response_msg.start_line.code = http.HTTP_OK - self.response_msg.body = encoder.Encode(result) self.response_msg.headers = handler_context.resp_headers - self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE + self.response_msg.body = result finally: # No reason to keep this any longer, even for exceptions handler_context.private = None @@ -418,7 +438,8 @@ class HttpServerRequestExecutor(object): """ return self.error_message_format % values -class HttpServer(http.HttpBase): + +class HttpServer(http.HttpBase, asyncore.dispatcher): """Generic HTTP server class Users of this class must subclass it and override the HandleRequest function. @@ -448,6 +469,7 @@ class HttpServer(http.HttpBase): """ http.HttpBase.__init__(self) + asyncore.dispatcher.__init__(self) if request_executor_class is None: self.request_executor = HttpServerRequestExecutor @@ -457,15 +479,15 @@ class HttpServer(http.HttpBase): self.mainloop = mainloop self.local_address = local_address self.port = port - - self.socket = self._CreateSocket(ssl_params, ssl_verify_peer) + family = netutils.IPAddress.GetAddressFamily(local_address) + self.socket = self._CreateSocket(ssl_params, ssl_verify_peer, family) # Allow port to be reused self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._children = [] - - mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN) + self.set_socket(self.socket) + self.accepting = True mainloop.RegisterSignal(self) def Start(self): @@ -475,9 +497,8 @@ class HttpServer(http.HttpBase): def Stop(self): self.socket.close() - def OnIO(self, fd, condition): - if condition & select.POLLIN: - self._IncomingConnection() + def handle_accept(self): + self._IncomingConnection() def OnSignal(self, signum): if signum == signal.SIGCHLD: @@ -498,7 +519,7 @@ class HttpServer(http.HttpBase): # 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) + pid, _ = os.waitpid(0, 0) except os.error: pid = None if pid and pid in self._children: @@ -506,7 +527,7 @@ class HttpServer(http.HttpBase): for child in self._children: try: - pid, status = os.waitpid(child, os.WNOHANG) + pid, _ = os.waitpid(child, os.WNOHANG) except os.error: pid = None if pid and pid in self._children: @@ -516,6 +537,7 @@ class HttpServer(http.HttpBase): """Called for each incoming connection """ + # pylint: disable-msg=W0212 (connection, client_addr) = self.socket.accept() self._CollectChildren(False) @@ -524,8 +546,21 @@ class HttpServer(http.HttpBase): if pid == 0: # Child process try: + # The client shouldn't keep the listening socket open. If the parent + # process is restarted, it would fail when there's already something + # listening (in this case its own child from a previous run) on the + # same port. + try: + self.socket.close() + except socket.error: + pass + self.socket = None + + # In case the handler code uses temporary files + utils.ResetTempfileModule() + self.request_executor(self, connection, client_addr) - except Exception: + except Exception: # pylint: disable-msg=W0703 logging.exception("Error while handling request from %s:%s", client_addr[0], client_addr[1]) os._exit(1)