Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ 200e38ac

History | View | Annotate | Download (16.9 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 socket
30 02cab3e7 Michael Hanselmann
import time
31 02cab3e7 Michael Hanselmann
import signal
32 112d240d Guido Trotter
import asyncore
33 02cab3e7 Michael Hanselmann
34 02cab3e7 Michael Hanselmann
from ganeti import http
35 02cab3e7 Michael Hanselmann
36 02cab3e7 Michael Hanselmann
37 02cab3e7 Michael Hanselmann
WEEKDAYNAME = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
38 02cab3e7 Michael Hanselmann
MONTHNAME = [None,
39 02cab3e7 Michael Hanselmann
             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
40 02cab3e7 Michael Hanselmann
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
41 02cab3e7 Michael Hanselmann
42 02cab3e7 Michael Hanselmann
# Default error message
43 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_CONTENT_TYPE = "text/html"
44 02cab3e7 Michael Hanselmann
DEFAULT_ERROR_MESSAGE = """\
45 02cab3e7 Michael Hanselmann
<html>
46 02cab3e7 Michael Hanselmann
<head>
47 02cab3e7 Michael Hanselmann
<title>Error response</title>
48 02cab3e7 Michael Hanselmann
</head>
49 02cab3e7 Michael Hanselmann
<body>
50 02cab3e7 Michael Hanselmann
<h1>Error response</h1>
51 02cab3e7 Michael Hanselmann
<p>Error code %(code)d.
52 02cab3e7 Michael Hanselmann
<p>Message: %(message)s.
53 02cab3e7 Michael Hanselmann
<p>Error code explanation: %(code)s = %(explain)s.
54 02cab3e7 Michael Hanselmann
</body>
55 02cab3e7 Michael Hanselmann
</html>
56 02cab3e7 Michael Hanselmann
"""
57 02cab3e7 Michael Hanselmann
58 02cab3e7 Michael Hanselmann
59 f30ca1e6 Michael Hanselmann
def _DateTimeHeader(gmnow=None):
60 02cab3e7 Michael Hanselmann
  """Return the current date and time formatted for a message header.
61 02cab3e7 Michael Hanselmann

62 f30ca1e6 Michael Hanselmann
  The time MUST be in the GMT timezone.
63 f30ca1e6 Michael Hanselmann

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

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

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

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

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

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

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

156 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
157 02cab3e7 Michael Hanselmann

158 02cab3e7 Michael Hanselmann
    @type start_line: string
159 02cab3e7 Michael Hanselmann
    @param start_line: Start line
160 02cab3e7 Michael Hanselmann

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

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

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

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

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

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

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

385 02cab3e7 Michael Hanselmann
    @type err: HttpException
386 02cab3e7 Michael Hanselmann
    @param err: Exception instance
387 02cab3e7 Michael Hanselmann

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

418 1f8588f6 Iustin Pop
    @type values: dict
419 1f8588f6 Iustin Pop
    @param values: dictionary with keys code, message and explain.
420 1f8588f6 Iustin Pop
    @rtype: string
421 1f8588f6 Iustin Pop
    @return: the body of the message
422 1f8588f6 Iustin Pop

423 1f8588f6 Iustin Pop
    """
424 1f8588f6 Iustin Pop
    return self.error_message_format % values
425 02cab3e7 Michael Hanselmann
426 57fd6d0b Michael Hanselmann
427 112d240d Guido Trotter
class HttpServer(http.HttpBase, asyncore.dispatcher):
428 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
429 02cab3e7 Michael Hanselmann

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

432 02cab3e7 Michael Hanselmann
  """
433 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
434 02cab3e7 Michael Hanselmann
435 02cab3e7 Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
436 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
437 1f8588f6 Iustin Pop
               request_executor_class=None):
438 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
439 02cab3e7 Michael Hanselmann

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

455 02cab3e7 Michael Hanselmann
    """
456 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
457 112d240d Guido Trotter
    asyncore.dispatcher.__init__(self)
458 02cab3e7 Michael Hanselmann
459 1f8588f6 Iustin Pop
    if request_executor_class is None:
460 1f8588f6 Iustin Pop
      self.request_executor = HttpServerRequestExecutor
461 1f8588f6 Iustin Pop
    else:
462 1f8588f6 Iustin Pop
      self.request_executor = request_executor_class
463 1f8588f6 Iustin Pop
464 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
465 02cab3e7 Michael Hanselmann
    self.local_address = local_address
466 02cab3e7 Michael Hanselmann
    self.port = port
467 02cab3e7 Michael Hanselmann
468 02cab3e7 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
469 02cab3e7 Michael Hanselmann
470 02cab3e7 Michael Hanselmann
    # Allow port to be reused
471 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
472 02cab3e7 Michael Hanselmann
473 02cab3e7 Michael Hanselmann
    self._children = []
474 112d240d Guido Trotter
    self.set_socket(self.socket)
475 112d240d Guido Trotter
    self.accepting = True
476 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
477 02cab3e7 Michael Hanselmann
478 02cab3e7 Michael Hanselmann
  def Start(self):
479 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
480 59305197 Michael Hanselmann
    self.socket.listen(1024)
481 02cab3e7 Michael Hanselmann
482 02cab3e7 Michael Hanselmann
  def Stop(self):
483 02cab3e7 Michael Hanselmann
    self.socket.close()
484 02cab3e7 Michael Hanselmann
485 112d240d Guido Trotter
  def handle_accept(self):
486 112d240d Guido Trotter
    self._IncomingConnection()
487 02cab3e7 Michael Hanselmann
488 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
489 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
490 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
491 02cab3e7 Michael Hanselmann
492 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
493 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
494 02cab3e7 Michael Hanselmann

495 02cab3e7 Michael Hanselmann
    @type quick: bool
496 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
497 02cab3e7 Michael Hanselmann

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

524 02cab3e7 Michael Hanselmann
    """
525 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
526 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
527 02cab3e7 Michael Hanselmann
528 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
529 02cab3e7 Michael Hanselmann
530 02cab3e7 Michael Hanselmann
    pid = os.fork()
531 02cab3e7 Michael Hanselmann
    if pid == 0:
532 02cab3e7 Michael Hanselmann
      # Child process
533 02cab3e7 Michael Hanselmann
      try:
534 bcb1a39e Michael Hanselmann
        # The client shouldn't keep the listening socket open. If the parent
535 bcb1a39e Michael Hanselmann
        # process is restarted, it would fail when there's already something
536 bcb1a39e Michael Hanselmann
        # listening (in this case its own child from a previous run) on the
537 bcb1a39e Michael Hanselmann
        # same port.
538 bcb1a39e Michael Hanselmann
        try:
539 bcb1a39e Michael Hanselmann
          self.socket.close()
540 bcb1a39e Michael Hanselmann
        except socket.error:
541 bcb1a39e Michael Hanselmann
          pass
542 bcb1a39e Michael Hanselmann
        self.socket = None
543 bcb1a39e Michael Hanselmann
544 1f8588f6 Iustin Pop
        self.request_executor(self, connection, client_addr)
545 7260cfbe Iustin Pop
      except Exception: # pylint: disable-msg=W0703
546 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
547 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
548 02cab3e7 Michael Hanselmann
        os._exit(1)
549 02cab3e7 Michael Hanselmann
      os._exit(0)
550 02cab3e7 Michael Hanselmann
    else:
551 02cab3e7 Michael Hanselmann
      self._children.append(pid)
552 02cab3e7 Michael Hanselmann
553 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
554 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
555 f8bd7df3 Michael Hanselmann

556 5bbd3f7f Michael Hanselmann
    Can be overridden by a subclass.
557 f8bd7df3 Michael Hanselmann

558 f8bd7df3 Michael Hanselmann
    """
559 f8bd7df3 Michael Hanselmann
560 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
561 02cab3e7 Michael Hanselmann
    """Handles a request.
562 02cab3e7 Michael Hanselmann

563 5bbd3f7f Michael Hanselmann
    Must be overridden by subclass.
564 02cab3e7 Michael Hanselmann

565 02cab3e7 Michael Hanselmann
    """
566 02cab3e7 Michael Hanselmann
    raise NotImplementedError()