Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ 81b59aaf

History | View | Annotate | Download (16.2 kB)

1 02cab3e7 Michael Hanselmann
#
2 02cab3e7 Michael Hanselmann
#
3 02cab3e7 Michael Hanselmann
4 02cab3e7 Michael Hanselmann
# Copyright (C) 2007, 2008 Google Inc.
5 02cab3e7 Michael Hanselmann
#
6 02cab3e7 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 02cab3e7 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 02cab3e7 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 02cab3e7 Michael Hanselmann
# (at your option) any later version.
10 02cab3e7 Michael Hanselmann
#
11 02cab3e7 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 02cab3e7 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 02cab3e7 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 02cab3e7 Michael Hanselmann
# General Public License for more details.
15 02cab3e7 Michael Hanselmann
#
16 02cab3e7 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 02cab3e7 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 02cab3e7 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02cab3e7 Michael Hanselmann
# 02110-1301, USA.
20 02cab3e7 Michael Hanselmann
21 02cab3e7 Michael Hanselmann
"""HTTP server module.
22 02cab3e7 Michael Hanselmann

23 02cab3e7 Michael Hanselmann
"""
24 02cab3e7 Michael Hanselmann
25 02cab3e7 Michael Hanselmann
import BaseHTTPServer
26 02cab3e7 Michael Hanselmann
import cgi
27 02cab3e7 Michael Hanselmann
import logging
28 02cab3e7 Michael Hanselmann
import os
29 02cab3e7 Michael Hanselmann
import select
30 02cab3e7 Michael Hanselmann
import socket
31 02cab3e7 Michael Hanselmann
import time
32 02cab3e7 Michael Hanselmann
import signal
33 02cab3e7 Michael Hanselmann
34 02cab3e7 Michael Hanselmann
from ganeti import constants
35 02cab3e7 Michael Hanselmann
from ganeti import serializer
36 02cab3e7 Michael Hanselmann
from ganeti import utils
37 02cab3e7 Michael Hanselmann
from ganeti import http
38 02cab3e7 Michael Hanselmann
39 02cab3e7 Michael Hanselmann
40 02cab3e7 Michael Hanselmann
WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
41 02cab3e7 Michael Hanselmann
MONTHNAME = [None,
42 02cab3e7 Michael Hanselmann
             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
43 02cab3e7 Michael Hanselmann
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
44 02cab3e7 Michael Hanselmann
45 02cab3e7 Michael Hanselmann
# Default error message
46 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
47 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_MESSAGE = """\
48 02cab3e7 Michael Hanselmann
<html>
49 02cab3e7 Michael Hanselmann
<head>
50 02cab3e7 Michael Hanselmann
<title>Error response</title>
51 02cab3e7 Michael Hanselmann
</head>
52 02cab3e7 Michael Hanselmann
<body>
53 02cab3e7 Michael Hanselmann
<h1>Error response</h1>
54 02cab3e7 Michael Hanselmann
<p>Error code %(code)d.
55 02cab3e7 Michael Hanselmann
<p>Message: %(message)s.
56 02cab3e7 Michael Hanselmann
<p>Error code explanation: %(code)s = %(explain)s.
57 02cab3e7 Michael Hanselmann
</body>
58 02cab3e7 Michael Hanselmann
</html>
59 02cab3e7 Michael Hanselmann
"""
60 02cab3e7 Michael Hanselmann
61 02cab3e7 Michael Hanselmann
62 f30ca1e6 Michael Hanselmann
def _DateTimeHeader(gmnow=None):
63 02cab3e7 Michael Hanselmann
  """Return the current date and time formatted for a message header.
64 02cab3e7 Michael Hanselmann

65 f30ca1e6 Michael Hanselmann
  The time MUST be in the GMT timezone.
66 f30ca1e6 Michael Hanselmann

67 02cab3e7 Michael Hanselmann
  """
68 f30ca1e6 Michael Hanselmann
  if gmnow is None:
69 f30ca1e6 Michael Hanselmann
    gmnow = time.gmtime()
70 f30ca1e6 Michael Hanselmann
  (year, month, day, hh, mm, ss, wd, _, _) = gmnow
71 02cab3e7 Michael Hanselmann
  return ("%s, %02d %3s %4d %02d:%02d:%02d GMT" %
72 02cab3e7 Michael Hanselmann
          (WEEKDAYNAME[wd], day, MONTHNAME[month], year, hh, mm, ss))
73 02cab3e7 Michael Hanselmann
74 02cab3e7 Michael Hanselmann
75 02cab3e7 Michael Hanselmann
class _HttpServerRequest(object):
76 02cab3e7 Michael Hanselmann
  """Data structure for HTTP request on server side.
77 02cab3e7 Michael Hanselmann

78 02cab3e7 Michael Hanselmann
  """
79 02cab3e7 Michael Hanselmann
  def __init__(self, request_msg):
80 02cab3e7 Michael Hanselmann
    # Request attributes
81 02cab3e7 Michael Hanselmann
    self.request_method = request_msg.start_line.method
82 02cab3e7 Michael Hanselmann
    self.request_path = request_msg.start_line.path
83 02cab3e7 Michael Hanselmann
    self.request_headers = request_msg.headers
84 02cab3e7 Michael Hanselmann
    self.request_body = request_msg.decoded_body
85 02cab3e7 Michael Hanselmann
86 02cab3e7 Michael Hanselmann
    # Response attributes
87 02cab3e7 Michael Hanselmann
    self.resp_headers = {}
88 02cab3e7 Michael Hanselmann
89 68fa9caf Michael Hanselmann
    # Private data for request handler (useful in combination with
90 68fa9caf Michael Hanselmann
    # authentication)
91 68fa9caf Michael Hanselmann
    self.private = None
92 68fa9caf Michael Hanselmann
93 02cab3e7 Michael Hanselmann
94 02cab3e7 Michael Hanselmann
class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
95 02cab3e7 Michael Hanselmann
  """Writes an HTTP response to client.
96 02cab3e7 Michael Hanselmann

97 02cab3e7 Michael Hanselmann
  """
98 02cab3e7 Michael Hanselmann
  def __init__(self, sock, request_msg, response_msg, write_timeout):
99 358a8811 Michael Hanselmann
    """Writes the response to the client.
100 358a8811 Michael Hanselmann

101 358a8811 Michael Hanselmann
    @type sock: socket
102 358a8811 Michael Hanselmann
    @param sock: Target socket
103 358a8811 Michael Hanselmann
    @type request_msg: http.HttpMessage
104 358a8811 Michael Hanselmann
    @param request_msg: Request message, required to determine whether
105 25e7b43f Iustin Pop
        response may have a message body
106 358a8811 Michael Hanselmann
    @type response_msg: http.HttpMessage
107 358a8811 Michael Hanselmann
    @param response_msg: Response message
108 358a8811 Michael Hanselmann
    @type write_timeout: float
109 358a8811 Michael Hanselmann
    @param write_timeout: Write timeout for socket
110 02cab3e7 Michael Hanselmann

111 02cab3e7 Michael Hanselmann
    """
112 02cab3e7 Michael Hanselmann
    self._request_msg = request_msg
113 02cab3e7 Michael Hanselmann
    self._response_msg = response_msg
114 02cab3e7 Michael Hanselmann
    http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
115 02cab3e7 Michael Hanselmann
116 02cab3e7 Michael Hanselmann
  def HasMessageBody(self):
117 02cab3e7 Michael Hanselmann
    """Logic to detect whether response should contain a message body.
118 02cab3e7 Michael Hanselmann

119 02cab3e7 Michael Hanselmann
    """
120 02cab3e7 Michael Hanselmann
    if self._request_msg.start_line:
121 02cab3e7 Michael Hanselmann
      request_method = self._request_msg.start_line.method
122 02cab3e7 Michael Hanselmann
    else:
123 02cab3e7 Michael Hanselmann
      request_method = None
124 02cab3e7 Michael Hanselmann
125 02cab3e7 Michael Hanselmann
    response_code = self._response_msg.start_line.code
126 02cab3e7 Michael Hanselmann
127 02cab3e7 Michael Hanselmann
    # RFC2616, section 4.3: "A message-body MUST NOT be included in a request
128 02cab3e7 Michael Hanselmann
    # if the specification of the request method (section 5.1.1) does not allow
129 02cab3e7 Michael Hanselmann
    # sending an entity-body in requests"
130 02cab3e7 Michael Hanselmann
    #
131 02cab3e7 Michael Hanselmann
    # RFC2616, section 9.4: "The HEAD method is identical to GET except that
132 02cab3e7 Michael Hanselmann
    # the server MUST NOT return a message-body in the response."
133 02cab3e7 Michael Hanselmann
    #
134 02cab3e7 Michael Hanselmann
    # RFC2616, section 10.2.5: "The 204 response MUST NOT include a
135 02cab3e7 Michael Hanselmann
    # message-body [...]"
136 02cab3e7 Michael Hanselmann
    #
137 02cab3e7 Michael Hanselmann
    # RFC2616, section 10.3.5: "The 304 response MUST NOT contain a
138 02cab3e7 Michael Hanselmann
    # message-body, [...]"
139 02cab3e7 Michael Hanselmann
140 02cab3e7 Michael Hanselmann
    return (http.HttpMessageWriter.HasMessageBody(self) and
141 3f3dfc15 Iustin Pop
            (request_method is not None and
142 3f3dfc15 Iustin Pop
             request_method != http.HTTP_HEAD) and
143 02cab3e7 Michael Hanselmann
            response_code >= http.HTTP_OK and
144 3f3dfc15 Iustin Pop
            response_code not in (http.HTTP_NO_CONTENT,
145 3f3dfc15 Iustin Pop
                                  http.HTTP_NOT_MODIFIED))
146 02cab3e7 Michael Hanselmann
147 02cab3e7 Michael Hanselmann
148 02cab3e7 Michael Hanselmann
class _HttpClientToServerMessageReader(http.HttpMessageReader):
149 02cab3e7 Michael Hanselmann
  """Reads an HTTP request sent by client.
150 02cab3e7 Michael Hanselmann

151 02cab3e7 Michael Hanselmann
  """
152 02cab3e7 Michael Hanselmann
  # Length limits
153 02cab3e7 Michael Hanselmann
  START_LINE_LENGTH_MAX = 4096
154 02cab3e7 Michael Hanselmann
  HEADER_LENGTH_MAX = 4096
155 02cab3e7 Michael Hanselmann
156 02cab3e7 Michael Hanselmann
  def ParseStartLine(self, start_line):
157 02cab3e7 Michael Hanselmann
    """Parses the start line sent by client.
158 02cab3e7 Michael Hanselmann

159 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
160 02cab3e7 Michael Hanselmann

161 02cab3e7 Michael Hanselmann
    @type start_line: string
162 02cab3e7 Michael Hanselmann
    @param start_line: Start line
163 02cab3e7 Michael Hanselmann

164 02cab3e7 Michael Hanselmann
    """
165 02cab3e7 Michael Hanselmann
    # Empty lines are skipped when reading
166 02cab3e7 Michael Hanselmann
    assert start_line
167 02cab3e7 Michael Hanselmann
168 02cab3e7 Michael Hanselmann
    logging.debug("HTTP request: %s", start_line)
169 02cab3e7 Michael Hanselmann
170 02cab3e7 Michael Hanselmann
    words = start_line.split()
171 02cab3e7 Michael Hanselmann
172 02cab3e7 Michael Hanselmann
    if len(words) == 3:
173 02cab3e7 Michael Hanselmann
      [method, path, version] = words
174 02cab3e7 Michael Hanselmann
      if version[:5] != 'HTTP/':
175 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad request version (%r)" % version)
176 02cab3e7 Michael Hanselmann
177 02cab3e7 Michael Hanselmann
      try:
178 02cab3e7 Michael Hanselmann
        base_version_number = version.split("/", 1)[1]
179 02cab3e7 Michael Hanselmann
        version_number = base_version_number.split(".")
180 02cab3e7 Michael Hanselmann
181 02cab3e7 Michael Hanselmann
        # RFC 2145 section 3.1 says there can be only one "." and
182 02cab3e7 Michael Hanselmann
        #   - major and minor numbers MUST be treated as
183 02cab3e7 Michael Hanselmann
        #      separate integers;
184 02cab3e7 Michael Hanselmann
        #   - HTTP/2.4 is a lower version than HTTP/2.13, which in
185 02cab3e7 Michael Hanselmann
        #      turn is lower than HTTP/12.3;
186 02cab3e7 Michael Hanselmann
        #   - Leading zeros MUST be ignored by recipients.
187 02cab3e7 Michael Hanselmann
        if len(version_number) != 2:
188 02cab3e7 Michael Hanselmann
          raise http.HttpBadRequest("Bad request version (%r)" % version)
189 02cab3e7 Michael Hanselmann
190 02cab3e7 Michael Hanselmann
        version_number = (int(version_number[0]), int(version_number[1]))
191 02cab3e7 Michael Hanselmann
      except (ValueError, IndexError):
192 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad request version (%r)" % version)
193 02cab3e7 Michael Hanselmann
194 02cab3e7 Michael Hanselmann
      if version_number >= (2, 0):
195 02cab3e7 Michael Hanselmann
        raise http.HttpVersionNotSupported("Invalid HTTP Version (%s)" %
196 02cab3e7 Michael Hanselmann
                                      base_version_number)
197 02cab3e7 Michael Hanselmann
198 02cab3e7 Michael Hanselmann
    elif len(words) == 2:
199 02cab3e7 Michael Hanselmann
      version = http.HTTP_0_9
200 02cab3e7 Michael Hanselmann
      [method, path] = words
201 02cab3e7 Michael Hanselmann
      if method != http.HTTP_GET:
202 02cab3e7 Michael Hanselmann
        raise http.HttpBadRequest("Bad HTTP/0.9 request type (%r)" % method)
203 02cab3e7 Michael Hanselmann
204 02cab3e7 Michael Hanselmann
    else:
205 02cab3e7 Michael Hanselmann
      raise http.HttpBadRequest("Bad request syntax (%r)" % start_line)
206 02cab3e7 Michael Hanselmann
207 02cab3e7 Michael Hanselmann
    return http.HttpClientToServerStartLine(method, path, version)
208 02cab3e7 Michael Hanselmann
209 02cab3e7 Michael Hanselmann
210 1f8588f6 Iustin Pop
class HttpServerRequestExecutor(object):
211 02cab3e7 Michael Hanselmann
  """Implements server side of HTTP.
212 02cab3e7 Michael Hanselmann

213 25e7b43f Iustin Pop
  This class implements the server side of HTTP. It's based on code of
214 25e7b43f Iustin Pop
  Python's BaseHTTPServer, from both version 2.4 and 3k. It does not
215 25e7b43f Iustin Pop
  support non-ASCII character encodings. Keep-alive connections are
216 25e7b43f Iustin Pop
  not supported.
217 02cab3e7 Michael Hanselmann

218 02cab3e7 Michael Hanselmann
  """
219 02cab3e7 Michael Hanselmann
  # The default request version.  This only affects responses up until
220 02cab3e7 Michael Hanselmann
  # the point where the request line is parsed, so it mainly decides what
221 02cab3e7 Michael Hanselmann
  # the client gets back when sending a malformed request line.
222 02cab3e7 Michael Hanselmann
  # Most web servers default to HTTP 0.9, i.e. don't send a status line.
223 02cab3e7 Michael Hanselmann
  default_request_version = http.HTTP_0_9
224 02cab3e7 Michael Hanselmann
225 02cab3e7 Michael Hanselmann
  # Error message settings
226 02cab3e7 Michael Hanselmann
  error_message_format = DEFAULT_ERROR_MESSAGE
227 02cab3e7 Michael Hanselmann
  error_content_type = DEFAULT_ERROR_CONTENT_TYPE
228 02cab3e7 Michael Hanselmann
229 02cab3e7 Michael Hanselmann
  responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
230 02cab3e7 Michael Hanselmann
231 02cab3e7 Michael Hanselmann
  # Timeouts in seconds for socket layer
232 02cab3e7 Michael Hanselmann
  WRITE_TIMEOUT = 10
233 02cab3e7 Michael Hanselmann
  READ_TIMEOUT = 10
234 02cab3e7 Michael Hanselmann
  CLOSE_TIMEOUT = 1
235 02cab3e7 Michael Hanselmann
236 02cab3e7 Michael Hanselmann
  def __init__(self, server, sock, client_addr):
237 02cab3e7 Michael Hanselmann
    """Initializes this class.
238 02cab3e7 Michael Hanselmann

239 02cab3e7 Michael Hanselmann
    """
240 02cab3e7 Michael Hanselmann
    self.server = server
241 02cab3e7 Michael Hanselmann
    self.sock = sock
242 02cab3e7 Michael Hanselmann
    self.client_addr = client_addr
243 02cab3e7 Michael Hanselmann
244 02cab3e7 Michael Hanselmann
    self.request_msg = http.HttpMessage()
245 02cab3e7 Michael Hanselmann
    self.response_msg = http.HttpMessage()
246 02cab3e7 Michael Hanselmann
247 02cab3e7 Michael Hanselmann
    self.response_msg.start_line = \
248 02cab3e7 Michael Hanselmann
      http.HttpServerToClientStartLine(version=self.default_request_version,
249 02cab3e7 Michael Hanselmann
                                       code=None, reason=None)
250 02cab3e7 Michael Hanselmann
251 02cab3e7 Michael Hanselmann
    # Disable Python's timeout
252 02cab3e7 Michael Hanselmann
    self.sock.settimeout(None)
253 02cab3e7 Michael Hanselmann
254 02cab3e7 Michael Hanselmann
    # Operate in non-blocking mode
255 02cab3e7 Michael Hanselmann
    self.sock.setblocking(0)
256 02cab3e7 Michael Hanselmann
257 14d57a8b Iustin Pop
    logging.debug("Connection from %s:%s", client_addr[0], client_addr[1])
258 02cab3e7 Michael Hanselmann
    try:
259 02cab3e7 Michael Hanselmann
      request_msg_reader = None
260 02cab3e7 Michael Hanselmann
      force_close = True
261 02cab3e7 Michael Hanselmann
      try:
262 f2e13d55 Michael Hanselmann
        # Do the secret SSL handshake
263 f2e13d55 Michael Hanselmann
        if self.server.using_ssl:
264 f2e13d55 Michael Hanselmann
          self.sock.set_accept_state()
265 f2e13d55 Michael Hanselmann
          try:
266 aea0ed67 Michael Hanselmann
            http.Handshake(self.sock, self.WRITE_TIMEOUT)
267 f2e13d55 Michael Hanselmann
          except http.HttpSessionHandshakeUnexpectedEOF:
268 f2e13d55 Michael Hanselmann
            # Ignore rest
269 f2e13d55 Michael Hanselmann
            return
270 f2e13d55 Michael Hanselmann
271 02cab3e7 Michael Hanselmann
        try:
272 02cab3e7 Michael Hanselmann
          try:
273 02cab3e7 Michael Hanselmann
            request_msg_reader = self._ReadRequest()
274 02cab3e7 Michael Hanselmann
            self._HandleRequest()
275 02cab3e7 Michael Hanselmann
276 02cab3e7 Michael Hanselmann
            # Only wait for client to close if we didn't have any exception.
277 02cab3e7 Michael Hanselmann
            force_close = False
278 02cab3e7 Michael Hanselmann
          except http.HttpException, err:
279 02cab3e7 Michael Hanselmann
            self._SetErrorStatus(err)
280 02cab3e7 Michael Hanselmann
        finally:
281 02cab3e7 Michael Hanselmann
          # Try to send a response
282 02cab3e7 Michael Hanselmann
          self._SendResponse()
283 02cab3e7 Michael Hanselmann
      finally:
284 aea0ed67 Michael Hanselmann
        http.ShutdownConnection(sock, self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT,
285 02cab3e7 Michael Hanselmann
                                request_msg_reader, force_close)
286 02cab3e7 Michael Hanselmann
287 02cab3e7 Michael Hanselmann
      self.sock.close()
288 02cab3e7 Michael Hanselmann
      self.sock = None
289 02cab3e7 Michael Hanselmann
    finally:
290 14d57a8b Iustin Pop
      logging.debug("Disconnected %s:%s", client_addr[0], client_addr[1])
291 02cab3e7 Michael Hanselmann
292 02cab3e7 Michael Hanselmann
  def _ReadRequest(self):
293 02cab3e7 Michael Hanselmann
    """Reads a request sent by client.
294 02cab3e7 Michael Hanselmann

295 02cab3e7 Michael Hanselmann
    """
296 02cab3e7 Michael Hanselmann
    try:
297 02cab3e7 Michael Hanselmann
      request_msg_reader = \
298 02cab3e7 Michael Hanselmann
        _HttpClientToServerMessageReader(self.sock, self.request_msg,
299 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
300 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
301 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading request")
302 02cab3e7 Michael Hanselmann
    except socket.error, err:
303 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading request: %s" % err)
304 02cab3e7 Michael Hanselmann
305 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.version = self.request_msg.start_line.version
306 02cab3e7 Michael Hanselmann
307 02cab3e7 Michael Hanselmann
    return request_msg_reader
308 02cab3e7 Michael Hanselmann
309 02cab3e7 Michael Hanselmann
  def _HandleRequest(self):
310 02cab3e7 Michael Hanselmann
    """Calls the handler function for the current request.
311 02cab3e7 Michael Hanselmann

312 02cab3e7 Michael Hanselmann
    """
313 02cab3e7 Michael Hanselmann
    handler_context = _HttpServerRequest(self.request_msg)
314 02cab3e7 Michael Hanselmann
315 02cab3e7 Michael Hanselmann
    try:
316 68fa9caf Michael Hanselmann
      try:
317 68fa9caf Michael Hanselmann
        # Authentication, etc.
318 68fa9caf Michael Hanselmann
        self.server.PreHandleRequest(handler_context)
319 68fa9caf Michael Hanselmann
320 68fa9caf Michael Hanselmann
        # Call actual request handler
321 68fa9caf Michael Hanselmann
        result = self.server.HandleRequest(handler_context)
322 68fa9caf Michael Hanselmann
      except (http.HttpException, KeyboardInterrupt, SystemExit):
323 68fa9caf Michael Hanselmann
        raise
324 68fa9caf Michael Hanselmann
      except Exception, err:
325 68fa9caf Michael Hanselmann
        logging.exception("Caught exception")
326 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message=str(err))
327 68fa9caf Michael Hanselmann
      except:
328 68fa9caf Michael Hanselmann
        logging.exception("Unknown exception")
329 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message="Unknown error")
330 68fa9caf Michael Hanselmann
331 68fa9caf Michael Hanselmann
      # TODO: Content-type
332 68fa9caf Michael Hanselmann
      encoder = http.HttpJsonConverter()
333 68fa9caf Michael Hanselmann
      self.response_msg.start_line.code = http.HTTP_OK
334 68fa9caf Michael Hanselmann
      self.response_msg.body = encoder.Encode(result)
335 68fa9caf Michael Hanselmann
      self.response_msg.headers = handler_context.resp_headers
336 68fa9caf Michael Hanselmann
      self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE
337 68fa9caf Michael Hanselmann
    finally:
338 68fa9caf Michael Hanselmann
      # No reason to keep this any longer, even for exceptions
339 68fa9caf Michael Hanselmann
      handler_context.private = None
340 02cab3e7 Michael Hanselmann
341 02cab3e7 Michael Hanselmann
  def _SendResponse(self):
342 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
343 02cab3e7 Michael Hanselmann

344 02cab3e7 Michael Hanselmann
    """
345 02cab3e7 Michael Hanselmann
    if self.response_msg.start_line.code is None:
346 02cab3e7 Michael Hanselmann
      return
347 02cab3e7 Michael Hanselmann
348 02cab3e7 Michael Hanselmann
    if not self.response_msg.headers:
349 02cab3e7 Michael Hanselmann
      self.response_msg.headers = {}
350 02cab3e7 Michael Hanselmann
351 02cab3e7 Michael Hanselmann
    self.response_msg.headers.update({
352 02cab3e7 Michael Hanselmann
      # TODO: Keep-alive is not supported
353 02cab3e7 Michael Hanselmann
      http.HTTP_CONNECTION: "close",
354 02cab3e7 Michael Hanselmann
      http.HTTP_DATE: _DateTimeHeader(),
355 02cab3e7 Michael Hanselmann
      http.HTTP_SERVER: http.HTTP_GANETI_VERSION,
356 02cab3e7 Michael Hanselmann
      })
357 02cab3e7 Michael Hanselmann
358 02cab3e7 Michael Hanselmann
    # Get response reason based on code
359 02cab3e7 Michael Hanselmann
    response_code = self.response_msg.start_line.code
360 02cab3e7 Michael Hanselmann
    if response_code in self.responses:
361 02cab3e7 Michael Hanselmann
      response_reason = self.responses[response_code][0]
362 02cab3e7 Michael Hanselmann
    else:
363 02cab3e7 Michael Hanselmann
      response_reason = ""
364 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.reason = response_reason
365 02cab3e7 Michael Hanselmann
366 02cab3e7 Michael Hanselmann
    logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1],
367 02cab3e7 Michael Hanselmann
                 self.request_msg.start_line, response_code)
368 02cab3e7 Michael Hanselmann
369 02cab3e7 Michael Hanselmann
    try:
370 02cab3e7 Michael Hanselmann
      _HttpServerToClientMessageWriter(self.sock, self.request_msg,
371 02cab3e7 Michael Hanselmann
                                       self.response_msg, self.WRITE_TIMEOUT)
372 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
373 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending response")
374 02cab3e7 Michael Hanselmann
    except socket.error, err:
375 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending response: %s" % err)
376 02cab3e7 Michael Hanselmann
377 02cab3e7 Michael Hanselmann
  def _SetErrorStatus(self, err):
378 02cab3e7 Michael Hanselmann
    """Sets the response code and body from a HttpException.
379 02cab3e7 Michael Hanselmann

380 02cab3e7 Michael Hanselmann
    @type err: HttpException
381 02cab3e7 Michael Hanselmann
    @param err: Exception instance
382 02cab3e7 Michael Hanselmann

383 02cab3e7 Michael Hanselmann
    """
384 02cab3e7 Michael Hanselmann
    try:
385 02cab3e7 Michael Hanselmann
      (shortmsg, longmsg) = self.responses[err.code]
386 02cab3e7 Michael Hanselmann
    except KeyError:
387 02cab3e7 Michael Hanselmann
      shortmsg = longmsg = "Unknown"
388 02cab3e7 Michael Hanselmann
389 02cab3e7 Michael Hanselmann
    if err.message:
390 02cab3e7 Michael Hanselmann
      message = err.message
391 02cab3e7 Michael Hanselmann
    else:
392 02cab3e7 Michael Hanselmann
      message = shortmsg
393 02cab3e7 Michael Hanselmann
394 02cab3e7 Michael Hanselmann
    values = {
395 02cab3e7 Michael Hanselmann
      "code": err.code,
396 02cab3e7 Michael Hanselmann
      "message": cgi.escape(message),
397 02cab3e7 Michael Hanselmann
      "explain": longmsg,
398 02cab3e7 Michael Hanselmann
      }
399 02cab3e7 Michael Hanselmann
400 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.code = err.code
401 a8e01e9f Michael Hanselmann
402 a8e01e9f Michael Hanselmann
    headers = {}
403 a8e01e9f Michael Hanselmann
    if err.headers:
404 a8e01e9f Michael Hanselmann
      headers.update(err.headers)
405 a8e01e9f Michael Hanselmann
    headers[http.HTTP_CONTENT_TYPE] = self.error_content_type
406 a8e01e9f Michael Hanselmann
    self.response_msg.headers = headers
407 a8e01e9f Michael Hanselmann
408 1f8588f6 Iustin Pop
    self.response_msg.body = self._FormatErrorMessage(values)
409 02cab3e7 Michael Hanselmann
410 1f8588f6 Iustin Pop
  def _FormatErrorMessage(self, values):
411 1f8588f6 Iustin Pop
    """Formats the body of an error message.
412 1f8588f6 Iustin Pop

413 1f8588f6 Iustin Pop
    @type values: dict
414 1f8588f6 Iustin Pop
    @param values: dictionary with keys code, message and explain.
415 1f8588f6 Iustin Pop
    @rtype: string
416 1f8588f6 Iustin Pop
    @return: the body of the message
417 1f8588f6 Iustin Pop

418 1f8588f6 Iustin Pop
    """
419 1f8588f6 Iustin Pop
    return self.error_message_format % values
420 02cab3e7 Michael Hanselmann
421 f4322a1e Michael Hanselmann
class HttpServer(http.HttpBase):
422 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
423 02cab3e7 Michael Hanselmann

424 02cab3e7 Michael Hanselmann
  Users of this class must subclass it and override the HandleRequest function.
425 02cab3e7 Michael Hanselmann

426 02cab3e7 Michael Hanselmann
  """
427 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
428 02cab3e7 Michael Hanselmann
429 02cab3e7 Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
430 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
431 1f8588f6 Iustin Pop
               request_executor_class=None):
432 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
433 02cab3e7 Michael Hanselmann

434 02cab3e7 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
435 02cab3e7 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
436 c41eea6e Iustin Pop
    @type local_address: string
437 02cab3e7 Michael Hanselmann
    @param local_address: Local IP address to bind to
438 02cab3e7 Michael Hanselmann
    @type port: int
439 02cab3e7 Michael Hanselmann
    @param port: TCP port to listen on
440 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
441 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
442 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
443 25e7b43f Iustin Pop
    @param ssl_verify_peer: Whether to require client certificate
444 25e7b43f Iustin Pop
        and compare it with our certificate
445 1f8588f6 Iustin Pop
    @type request_executor_class: class
446 1f8588f6 Iustin Pop
    @param request_executor_class: an class derived from the
447 1f8588f6 Iustin Pop
        HttpServerRequestExecutor class
448 02cab3e7 Michael Hanselmann

449 02cab3e7 Michael Hanselmann
    """
450 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
451 02cab3e7 Michael Hanselmann
452 1f8588f6 Iustin Pop
    if request_executor_class is None:
453 1f8588f6 Iustin Pop
      self.request_executor = HttpServerRequestExecutor
454 1f8588f6 Iustin Pop
    else:
455 1f8588f6 Iustin Pop
      self.request_executor = request_executor_class
456 1f8588f6 Iustin Pop
457 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
458 02cab3e7 Michael Hanselmann
    self.local_address = local_address
459 02cab3e7 Michael Hanselmann
    self.port = port
460 02cab3e7 Michael Hanselmann
461 02cab3e7 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
462 02cab3e7 Michael Hanselmann
463 02cab3e7 Michael Hanselmann
    # Allow port to be reused
464 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
465 02cab3e7 Michael Hanselmann
466 02cab3e7 Michael Hanselmann
    self._children = []
467 02cab3e7 Michael Hanselmann
468 02cab3e7 Michael Hanselmann
    mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
469 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
470 02cab3e7 Michael Hanselmann
471 02cab3e7 Michael Hanselmann
  def Start(self):
472 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
473 59305197 Michael Hanselmann
    self.socket.listen(1024)
474 02cab3e7 Michael Hanselmann
475 02cab3e7 Michael Hanselmann
  def Stop(self):
476 02cab3e7 Michael Hanselmann
    self.socket.close()
477 02cab3e7 Michael Hanselmann
478 02cab3e7 Michael Hanselmann
  def OnIO(self, fd, condition):
479 02cab3e7 Michael Hanselmann
    if condition & select.POLLIN:
480 02cab3e7 Michael Hanselmann
      self._IncomingConnection()
481 02cab3e7 Michael Hanselmann
482 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
483 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
484 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
485 02cab3e7 Michael Hanselmann
486 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
487 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
488 02cab3e7 Michael Hanselmann

489 02cab3e7 Michael Hanselmann
    @type quick: bool
490 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
491 02cab3e7 Michael Hanselmann

492 02cab3e7 Michael Hanselmann
    """
493 02cab3e7 Michael Hanselmann
    if not quick:
494 02cab3e7 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
495 02cab3e7 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
496 02cab3e7 Michael Hanselmann
        try:
497 02cab3e7 Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
498 02cab3e7 Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
499 02cab3e7 Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
500 02cab3e7 Michael Hanselmann
          # and killing them after some time.
501 02cab3e7 Michael Hanselmann
          pid, status = os.waitpid(0, 0)
502 02cab3e7 Michael Hanselmann
        except os.error:
503 02cab3e7 Michael Hanselmann
          pid = None
504 02cab3e7 Michael Hanselmann
        if pid and pid in self._children:
505 02cab3e7 Michael Hanselmann
          self._children.remove(pid)
506 02cab3e7 Michael Hanselmann
507 02cab3e7 Michael Hanselmann
    for child in self._children:
508 02cab3e7 Michael Hanselmann
      try:
509 02cab3e7 Michael Hanselmann
        pid, status = os.waitpid(child, os.WNOHANG)
510 02cab3e7 Michael Hanselmann
      except os.error:
511 02cab3e7 Michael Hanselmann
        pid = None
512 02cab3e7 Michael Hanselmann
      if pid and pid in self._children:
513 02cab3e7 Michael Hanselmann
        self._children.remove(pid)
514 02cab3e7 Michael Hanselmann
515 02cab3e7 Michael Hanselmann
  def _IncomingConnection(self):
516 02cab3e7 Michael Hanselmann
    """Called for each incoming connection
517 02cab3e7 Michael Hanselmann

518 02cab3e7 Michael Hanselmann
    """
519 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
520 02cab3e7 Michael Hanselmann
521 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
522 02cab3e7 Michael Hanselmann
523 02cab3e7 Michael Hanselmann
    pid = os.fork()
524 02cab3e7 Michael Hanselmann
    if pid == 0:
525 02cab3e7 Michael Hanselmann
      # Child process
526 02cab3e7 Michael Hanselmann
      try:
527 1f8588f6 Iustin Pop
        self.request_executor(self, connection, client_addr)
528 02cab3e7 Michael Hanselmann
      except Exception:
529 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
530 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
531 02cab3e7 Michael Hanselmann
        os._exit(1)
532 02cab3e7 Michael Hanselmann
      os._exit(0)
533 02cab3e7 Michael Hanselmann
    else:
534 02cab3e7 Michael Hanselmann
      self._children.append(pid)
535 02cab3e7 Michael Hanselmann
536 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
537 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
538 f8bd7df3 Michael Hanselmann

539 f8bd7df3 Michael Hanselmann
    Can be overriden by a subclass.
540 f8bd7df3 Michael Hanselmann

541 f8bd7df3 Michael Hanselmann
    """
542 f8bd7df3 Michael Hanselmann
543 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
544 02cab3e7 Michael Hanselmann
    """Handles a request.
545 02cab3e7 Michael Hanselmann

546 02cab3e7 Michael Hanselmann
    Must be overriden by subclass.
547 02cab3e7 Michael Hanselmann

548 02cab3e7 Michael Hanselmann
    """
549 02cab3e7 Michael Hanselmann
    raise NotImplementedError()