Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ f2e13d55

History | View | Annotate | Download (15 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 02cab3e7 Michael Hanselmann
def _DateTimeHeader():
63 02cab3e7 Michael Hanselmann
  """Return the current date and time formatted for a message header.
64 02cab3e7 Michael Hanselmann

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

74 02cab3e7 Michael Hanselmann
  """
75 02cab3e7 Michael Hanselmann
  def __init__(self, request_msg):
76 02cab3e7 Michael Hanselmann
    # Request attributes
77 02cab3e7 Michael Hanselmann
    self.request_method = request_msg.start_line.method
78 02cab3e7 Michael Hanselmann
    self.request_path = request_msg.start_line.path
79 02cab3e7 Michael Hanselmann
    self.request_headers = request_msg.headers
80 02cab3e7 Michael Hanselmann
    self.request_body = request_msg.decoded_body
81 02cab3e7 Michael Hanselmann
82 02cab3e7 Michael Hanselmann
    # Response attributes
83 02cab3e7 Michael Hanselmann
    self.resp_headers = {}
84 02cab3e7 Michael Hanselmann
85 02cab3e7 Michael Hanselmann
86 02cab3e7 Michael Hanselmann
class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
87 02cab3e7 Michael Hanselmann
  """Writes an HTTP response to client.
88 02cab3e7 Michael Hanselmann

89 02cab3e7 Michael Hanselmann
  """
90 02cab3e7 Michael Hanselmann
  def __init__(self, sock, request_msg, response_msg, write_timeout):
91 358a8811 Michael Hanselmann
    """Writes the response to the client.
92 358a8811 Michael Hanselmann

93 358a8811 Michael Hanselmann
    @type sock: socket
94 358a8811 Michael Hanselmann
    @param sock: Target socket
95 358a8811 Michael Hanselmann
    @type request_msg: http.HttpMessage
96 358a8811 Michael Hanselmann
    @param request_msg: Request message, required to determine whether
97 358a8811 Michael Hanselmann
                        response may have a message body
98 358a8811 Michael Hanselmann
    @type response_msg: http.HttpMessage
99 358a8811 Michael Hanselmann
    @param response_msg: Response message
100 358a8811 Michael Hanselmann
    @type write_timeout: float
101 358a8811 Michael Hanselmann
    @param write_timeout: Write timeout for socket
102 02cab3e7 Michael Hanselmann

103 02cab3e7 Michael Hanselmann
    """
104 02cab3e7 Michael Hanselmann
    self._request_msg = request_msg
105 02cab3e7 Michael Hanselmann
    self._response_msg = response_msg
106 02cab3e7 Michael Hanselmann
    http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
107 02cab3e7 Michael Hanselmann
108 02cab3e7 Michael Hanselmann
  def HasMessageBody(self):
109 02cab3e7 Michael Hanselmann
    """Logic to detect whether response should contain a message body.
110 02cab3e7 Michael Hanselmann

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

143 02cab3e7 Michael Hanselmann
  """
144 02cab3e7 Michael Hanselmann
  # Length limits
145 02cab3e7 Michael Hanselmann
  START_LINE_LENGTH_MAX = 4096
146 02cab3e7 Michael Hanselmann
  HEADER_LENGTH_MAX = 4096
147 02cab3e7 Michael Hanselmann
148 02cab3e7 Michael Hanselmann
  def ParseStartLine(self, start_line):
149 02cab3e7 Michael Hanselmann
    """Parses the start line sent by client.
150 02cab3e7 Michael Hanselmann

151 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
152 02cab3e7 Michael Hanselmann

153 02cab3e7 Michael Hanselmann
    @type start_line: string
154 02cab3e7 Michael Hanselmann
    @param start_line: Start line
155 02cab3e7 Michael Hanselmann

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

205 02cab3e7 Michael Hanselmann
  This class implements the server side of HTTP. It's based on code of Python's
206 02cab3e7 Michael Hanselmann
  BaseHTTPServer, from both version 2.4 and 3k. It does not support non-ASCII
207 02cab3e7 Michael Hanselmann
  character encodings. Keep-alive connections are not supported.
208 02cab3e7 Michael Hanselmann

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

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

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

306 02cab3e7 Michael Hanselmann
    """
307 02cab3e7 Michael Hanselmann
    handler_context = _HttpServerRequest(self.request_msg)
308 02cab3e7 Michael Hanselmann
309 02cab3e7 Michael Hanselmann
    try:
310 02cab3e7 Michael Hanselmann
      result = self.server.HandleRequest(handler_context)
311 02cab3e7 Michael Hanselmann
    except (http.HttpException, KeyboardInterrupt, SystemExit):
312 02cab3e7 Michael Hanselmann
      raise
313 02cab3e7 Michael Hanselmann
    except Exception, err:
314 02cab3e7 Michael Hanselmann
      logging.exception("Caught exception")
315 02cab3e7 Michael Hanselmann
      raise http.HttpInternalError(message=str(err))
316 02cab3e7 Michael Hanselmann
    except:
317 02cab3e7 Michael Hanselmann
      logging.exception("Unknown exception")
318 02cab3e7 Michael Hanselmann
      raise http.HttpInternalError(message="Unknown error")
319 02cab3e7 Michael Hanselmann
320 02cab3e7 Michael Hanselmann
    # TODO: Content-type
321 02cab3e7 Michael Hanselmann
    encoder = http.HttpJsonConverter()
322 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.code = http.HTTP_OK
323 02cab3e7 Michael Hanselmann
    self.response_msg.body = encoder.Encode(result)
324 02cab3e7 Michael Hanselmann
    self.response_msg.headers = handler_context.resp_headers
325 02cab3e7 Michael Hanselmann
    self.response_msg.headers[http.HTTP_CONTENT_TYPE] = encoder.CONTENT_TYPE
326 02cab3e7 Michael Hanselmann
327 02cab3e7 Michael Hanselmann
  def _SendResponse(self):
328 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
329 02cab3e7 Michael Hanselmann

330 02cab3e7 Michael Hanselmann
    """
331 02cab3e7 Michael Hanselmann
    if self.response_msg.start_line.code is None:
332 02cab3e7 Michael Hanselmann
      return
333 02cab3e7 Michael Hanselmann
334 02cab3e7 Michael Hanselmann
    if not self.response_msg.headers:
335 02cab3e7 Michael Hanselmann
      self.response_msg.headers = {}
336 02cab3e7 Michael Hanselmann
337 02cab3e7 Michael Hanselmann
    self.response_msg.headers.update({
338 02cab3e7 Michael Hanselmann
      # TODO: Keep-alive is not supported
339 02cab3e7 Michael Hanselmann
      http.HTTP_CONNECTION: "close",
340 02cab3e7 Michael Hanselmann
      http.HTTP_DATE: _DateTimeHeader(),
341 02cab3e7 Michael Hanselmann
      http.HTTP_SERVER: http.HTTP_GANETI_VERSION,
342 02cab3e7 Michael Hanselmann
      })
343 02cab3e7 Michael Hanselmann
344 02cab3e7 Michael Hanselmann
    # Get response reason based on code
345 02cab3e7 Michael Hanselmann
    response_code = self.response_msg.start_line.code
346 02cab3e7 Michael Hanselmann
    if response_code in self.responses:
347 02cab3e7 Michael Hanselmann
      response_reason = self.responses[response_code][0]
348 02cab3e7 Michael Hanselmann
    else:
349 02cab3e7 Michael Hanselmann
      response_reason = ""
350 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.reason = response_reason
351 02cab3e7 Michael Hanselmann
352 02cab3e7 Michael Hanselmann
    logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1],
353 02cab3e7 Michael Hanselmann
                 self.request_msg.start_line, response_code)
354 02cab3e7 Michael Hanselmann
355 02cab3e7 Michael Hanselmann
    try:
356 02cab3e7 Michael Hanselmann
      _HttpServerToClientMessageWriter(self.sock, self.request_msg,
357 02cab3e7 Michael Hanselmann
                                       self.response_msg, self.WRITE_TIMEOUT)
358 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
359 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending response")
360 02cab3e7 Michael Hanselmann
    except socket.error, err:
361 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending response: %s" % err)
362 02cab3e7 Michael Hanselmann
363 02cab3e7 Michael Hanselmann
  def _SetErrorStatus(self, err):
364 02cab3e7 Michael Hanselmann
    """Sets the response code and body from a HttpException.
365 02cab3e7 Michael Hanselmann

366 02cab3e7 Michael Hanselmann
    @type err: HttpException
367 02cab3e7 Michael Hanselmann
    @param err: Exception instance
368 02cab3e7 Michael Hanselmann

369 02cab3e7 Michael Hanselmann
    """
370 02cab3e7 Michael Hanselmann
    try:
371 02cab3e7 Michael Hanselmann
      (shortmsg, longmsg) = self.responses[err.code]
372 02cab3e7 Michael Hanselmann
    except KeyError:
373 02cab3e7 Michael Hanselmann
      shortmsg = longmsg = "Unknown"
374 02cab3e7 Michael Hanselmann
375 02cab3e7 Michael Hanselmann
    if err.message:
376 02cab3e7 Michael Hanselmann
      message = err.message
377 02cab3e7 Michael Hanselmann
    else:
378 02cab3e7 Michael Hanselmann
      message = shortmsg
379 02cab3e7 Michael Hanselmann
380 02cab3e7 Michael Hanselmann
    values = {
381 02cab3e7 Michael Hanselmann
      "code": err.code,
382 02cab3e7 Michael Hanselmann
      "message": cgi.escape(message),
383 02cab3e7 Michael Hanselmann
      "explain": longmsg,
384 02cab3e7 Michael Hanselmann
      }
385 02cab3e7 Michael Hanselmann
386 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.code = err.code
387 02cab3e7 Michael Hanselmann
    self.response_msg.headers = {
388 02cab3e7 Michael Hanselmann
      http.HTTP_CONTENT_TYPE: self.error_content_type,
389 02cab3e7 Michael Hanselmann
      }
390 02cab3e7 Michael Hanselmann
    self.response_msg.body = self.error_message_format % values
391 02cab3e7 Michael Hanselmann
392 02cab3e7 Michael Hanselmann
393 f4322a1e Michael Hanselmann
class HttpServer(http.HttpBase):
394 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
395 02cab3e7 Michael Hanselmann

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

398 02cab3e7 Michael Hanselmann
  """
399 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
400 02cab3e7 Michael Hanselmann
401 02cab3e7 Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
402 02cab3e7 Michael Hanselmann
               ssl_params=None, ssl_verify_peer=False):
403 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
404 02cab3e7 Michael Hanselmann

405 02cab3e7 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
406 02cab3e7 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
407 c41eea6e Iustin Pop
    @type local_address: string
408 02cab3e7 Michael Hanselmann
    @param local_address: Local IP address to bind to
409 02cab3e7 Michael Hanselmann
    @type port: int
410 02cab3e7 Michael Hanselmann
    @param port: TCP port to listen on
411 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
412 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
413 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
414 02cab3e7 Michael Hanselmann
    @param ssl_verify_peer: Whether to require client certificate and compare
415 02cab3e7 Michael Hanselmann
                            it with our certificate
416 02cab3e7 Michael Hanselmann

417 02cab3e7 Michael Hanselmann
    """
418 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
419 02cab3e7 Michael Hanselmann
420 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
421 02cab3e7 Michael Hanselmann
    self.local_address = local_address
422 02cab3e7 Michael Hanselmann
    self.port = port
423 02cab3e7 Michael Hanselmann
424 02cab3e7 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
425 02cab3e7 Michael Hanselmann
426 02cab3e7 Michael Hanselmann
    # Allow port to be reused
427 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
428 02cab3e7 Michael Hanselmann
429 02cab3e7 Michael Hanselmann
    self._children = []
430 02cab3e7 Michael Hanselmann
431 02cab3e7 Michael Hanselmann
    mainloop.RegisterIO(self, self.socket.fileno(), select.POLLIN)
432 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
433 02cab3e7 Michael Hanselmann
434 02cab3e7 Michael Hanselmann
  def Start(self):
435 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
436 59305197 Michael Hanselmann
    self.socket.listen(1024)
437 02cab3e7 Michael Hanselmann
438 02cab3e7 Michael Hanselmann
  def Stop(self):
439 02cab3e7 Michael Hanselmann
    self.socket.close()
440 02cab3e7 Michael Hanselmann
441 02cab3e7 Michael Hanselmann
  def OnIO(self, fd, condition):
442 02cab3e7 Michael Hanselmann
    if condition & select.POLLIN:
443 02cab3e7 Michael Hanselmann
      self._IncomingConnection()
444 02cab3e7 Michael Hanselmann
445 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
446 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
447 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
448 02cab3e7 Michael Hanselmann
449 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
450 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
451 02cab3e7 Michael Hanselmann

452 02cab3e7 Michael Hanselmann
    @type quick: bool
453 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
454 02cab3e7 Michael Hanselmann

455 02cab3e7 Michael Hanselmann
    """
456 02cab3e7 Michael Hanselmann
    if not quick:
457 02cab3e7 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
458 02cab3e7 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
459 02cab3e7 Michael Hanselmann
        try:
460 02cab3e7 Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
461 02cab3e7 Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
462 02cab3e7 Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
463 02cab3e7 Michael Hanselmann
          # and killing them after some time.
464 02cab3e7 Michael Hanselmann
          pid, status = os.waitpid(0, 0)
465 02cab3e7 Michael Hanselmann
        except os.error:
466 02cab3e7 Michael Hanselmann
          pid = None
467 02cab3e7 Michael Hanselmann
        if pid and pid in self._children:
468 02cab3e7 Michael Hanselmann
          self._children.remove(pid)
469 02cab3e7 Michael Hanselmann
470 02cab3e7 Michael Hanselmann
    for child in self._children:
471 02cab3e7 Michael Hanselmann
      try:
472 02cab3e7 Michael Hanselmann
        pid, status = os.waitpid(child, os.WNOHANG)
473 02cab3e7 Michael Hanselmann
      except os.error:
474 02cab3e7 Michael Hanselmann
        pid = None
475 02cab3e7 Michael Hanselmann
      if pid and pid in self._children:
476 02cab3e7 Michael Hanselmann
        self._children.remove(pid)
477 02cab3e7 Michael Hanselmann
478 02cab3e7 Michael Hanselmann
  def _IncomingConnection(self):
479 02cab3e7 Michael Hanselmann
    """Called for each incoming connection
480 02cab3e7 Michael Hanselmann

481 02cab3e7 Michael Hanselmann
    """
482 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
483 02cab3e7 Michael Hanselmann
484 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
485 02cab3e7 Michael Hanselmann
486 02cab3e7 Michael Hanselmann
    pid = os.fork()
487 02cab3e7 Michael Hanselmann
    if pid == 0:
488 02cab3e7 Michael Hanselmann
      # Child process
489 02cab3e7 Michael Hanselmann
      try:
490 02cab3e7 Michael Hanselmann
        _HttpServerRequestExecutor(self, connection, client_addr)
491 02cab3e7 Michael Hanselmann
      except Exception:
492 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
493 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
494 02cab3e7 Michael Hanselmann
        os._exit(1)
495 02cab3e7 Michael Hanselmann
      os._exit(0)
496 02cab3e7 Michael Hanselmann
    else:
497 02cab3e7 Michael Hanselmann
      self._children.append(pid)
498 02cab3e7 Michael Hanselmann
499 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
500 02cab3e7 Michael Hanselmann
    """Handles a request.
501 02cab3e7 Michael Hanselmann

502 02cab3e7 Michael Hanselmann
    Must be overriden by subclass.
503 02cab3e7 Michael Hanselmann

504 02cab3e7 Michael Hanselmann
    """
505 02cab3e7 Michael Hanselmann
    raise NotImplementedError()