Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ 9f7b4967

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

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

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

76 02cab3e7 Michael Hanselmann
  """
77 a8950eb7 Michael Hanselmann
  def __init__(self, method, path, headers, body):
78 02cab3e7 Michael Hanselmann
    # Request attributes
79 a8950eb7 Michael Hanselmann
    self.request_method = method
80 a8950eb7 Michael Hanselmann
    self.request_path = path
81 a8950eb7 Michael Hanselmann
    self.request_headers = headers
82 a8950eb7 Michael Hanselmann
    self.request_body = body
83 02cab3e7 Michael Hanselmann
84 02cab3e7 Michael Hanselmann
    # Response attributes
85 02cab3e7 Michael Hanselmann
    self.resp_headers = {}
86 02cab3e7 Michael Hanselmann
87 68fa9caf Michael Hanselmann
    # Private data for request handler (useful in combination with
88 68fa9caf Michael Hanselmann
    # authentication)
89 68fa9caf Michael Hanselmann
    self.private = None
90 68fa9caf Michael Hanselmann
91 d44ea6a3 Michael Hanselmann
  def __repr__(self):
92 d44ea6a3 Michael Hanselmann
    status = ["%s.%s" % (self.__class__.__module__, self.__class__.__name__),
93 d44ea6a3 Michael Hanselmann
              self.request_method, self.request_path,
94 d44ea6a3 Michael Hanselmann
              "headers=%r" % str(self.request_headers),
95 d44ea6a3 Michael Hanselmann
              "body=%r" % (self.request_body, )]
96 d44ea6a3 Michael Hanselmann
97 d44ea6a3 Michael Hanselmann
    return "<%s at %#x>" % (" ".join(status), id(self))
98 d44ea6a3 Michael Hanselmann
99 02cab3e7 Michael Hanselmann
100 02cab3e7 Michael Hanselmann
class _HttpServerToClientMessageWriter(http.HttpMessageWriter):
101 02cab3e7 Michael Hanselmann
  """Writes an HTTP response to client.
102 02cab3e7 Michael Hanselmann

103 02cab3e7 Michael Hanselmann
  """
104 02cab3e7 Michael Hanselmann
  def __init__(self, sock, request_msg, response_msg, write_timeout):
105 358a8811 Michael Hanselmann
    """Writes the response to the client.
106 358a8811 Michael Hanselmann

107 358a8811 Michael Hanselmann
    @type sock: socket
108 358a8811 Michael Hanselmann
    @param sock: Target socket
109 358a8811 Michael Hanselmann
    @type request_msg: http.HttpMessage
110 358a8811 Michael Hanselmann
    @param request_msg: Request message, required to determine whether
111 25e7b43f Iustin Pop
        response may have a message body
112 358a8811 Michael Hanselmann
    @type response_msg: http.HttpMessage
113 358a8811 Michael Hanselmann
    @param response_msg: Response message
114 358a8811 Michael Hanselmann
    @type write_timeout: float
115 358a8811 Michael Hanselmann
    @param write_timeout: Write timeout for socket
116 02cab3e7 Michael Hanselmann

117 02cab3e7 Michael Hanselmann
    """
118 02cab3e7 Michael Hanselmann
    self._request_msg = request_msg
119 02cab3e7 Michael Hanselmann
    self._response_msg = response_msg
120 02cab3e7 Michael Hanselmann
    http.HttpMessageWriter.__init__(self, sock, response_msg, write_timeout)
121 02cab3e7 Michael Hanselmann
122 02cab3e7 Michael Hanselmann
  def HasMessageBody(self):
123 02cab3e7 Michael Hanselmann
    """Logic to detect whether response should contain a message body.
124 02cab3e7 Michael Hanselmann

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

157 02cab3e7 Michael Hanselmann
  """
158 02cab3e7 Michael Hanselmann
  # Length limits
159 02cab3e7 Michael Hanselmann
  START_LINE_LENGTH_MAX = 4096
160 02cab3e7 Michael Hanselmann
  HEADER_LENGTH_MAX = 4096
161 02cab3e7 Michael Hanselmann
162 02cab3e7 Michael Hanselmann
  def ParseStartLine(self, start_line):
163 02cab3e7 Michael Hanselmann
    """Parses the start line sent by client.
164 02cab3e7 Michael Hanselmann

165 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
166 02cab3e7 Michael Hanselmann

167 02cab3e7 Michael Hanselmann
    @type start_line: string
168 02cab3e7 Michael Hanselmann
    @param start_line: Start line
169 02cab3e7 Michael Hanselmann

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

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

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

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

309 02cab3e7 Michael Hanselmann
    """
310 02cab3e7 Michael Hanselmann
    try:
311 02cab3e7 Michael Hanselmann
      request_msg_reader = \
312 02cab3e7 Michael Hanselmann
        _HttpClientToServerMessageReader(self.sock, self.request_msg,
313 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
314 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
315 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading request")
316 02cab3e7 Michael Hanselmann
    except socket.error, err:
317 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading request: %s" % err)
318 02cab3e7 Michael Hanselmann
319 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.version = self.request_msg.start_line.version
320 02cab3e7 Michael Hanselmann
321 02cab3e7 Michael Hanselmann
    return request_msg_reader
322 02cab3e7 Michael Hanselmann
323 02cab3e7 Michael Hanselmann
  def _HandleRequest(self):
324 02cab3e7 Michael Hanselmann
    """Calls the handler function for the current request.
325 02cab3e7 Michael Hanselmann

326 02cab3e7 Michael Hanselmann
    """
327 a8950eb7 Michael Hanselmann
    handler_context = _HttpServerRequest(self.request_msg.start_line.method,
328 a8950eb7 Michael Hanselmann
                                         self.request_msg.start_line.path,
329 a8950eb7 Michael Hanselmann
                                         self.request_msg.headers,
330 bb3776b4 Michael Hanselmann
                                         self.request_msg.body)
331 02cab3e7 Michael Hanselmann
332 d44ea6a3 Michael Hanselmann
    logging.debug("Handling request %r", handler_context)
333 02cab3e7 Michael Hanselmann
334 02cab3e7 Michael Hanselmann
    try:
335 68fa9caf Michael Hanselmann
      try:
336 68fa9caf Michael Hanselmann
        # Authentication, etc.
337 68fa9caf Michael Hanselmann
        self.server.PreHandleRequest(handler_context)
338 68fa9caf Michael Hanselmann
339 68fa9caf Michael Hanselmann
        # Call actual request handler
340 68fa9caf Michael Hanselmann
        result = self.server.HandleRequest(handler_context)
341 68fa9caf Michael Hanselmann
      except (http.HttpException, KeyboardInterrupt, SystemExit):
342 68fa9caf Michael Hanselmann
        raise
343 68fa9caf Michael Hanselmann
      except Exception, err:
344 68fa9caf Michael Hanselmann
        logging.exception("Caught exception")
345 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message=str(err))
346 68fa9caf Michael Hanselmann
      except:
347 68fa9caf Michael Hanselmann
        logging.exception("Unknown exception")
348 68fa9caf Michael Hanselmann
        raise http.HttpInternalServerError(message="Unknown error")
349 68fa9caf Michael Hanselmann
350 ab221ddf Michael Hanselmann
      if not isinstance(result, basestring):
351 ab221ddf Michael Hanselmann
        raise http.HttpError("Handler function didn't return string type")
352 ab221ddf Michael Hanselmann
353 68fa9caf Michael Hanselmann
      self.response_msg.start_line.code = http.HTTP_OK
354 68fa9caf Michael Hanselmann
      self.response_msg.headers = handler_context.resp_headers
355 ab221ddf Michael Hanselmann
      self.response_msg.body = result
356 68fa9caf Michael Hanselmann
    finally:
357 68fa9caf Michael Hanselmann
      # No reason to keep this any longer, even for exceptions
358 68fa9caf Michael Hanselmann
      handler_context.private = None
359 02cab3e7 Michael Hanselmann
360 02cab3e7 Michael Hanselmann
  def _SendResponse(self):
361 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
362 02cab3e7 Michael Hanselmann

363 02cab3e7 Michael Hanselmann
    """
364 02cab3e7 Michael Hanselmann
    if self.response_msg.start_line.code is None:
365 02cab3e7 Michael Hanselmann
      return
366 02cab3e7 Michael Hanselmann
367 02cab3e7 Michael Hanselmann
    if not self.response_msg.headers:
368 02cab3e7 Michael Hanselmann
      self.response_msg.headers = {}
369 02cab3e7 Michael Hanselmann
370 02cab3e7 Michael Hanselmann
    self.response_msg.headers.update({
371 02cab3e7 Michael Hanselmann
      # TODO: Keep-alive is not supported
372 02cab3e7 Michael Hanselmann
      http.HTTP_CONNECTION: "close",
373 02cab3e7 Michael Hanselmann
      http.HTTP_DATE: _DateTimeHeader(),
374 02cab3e7 Michael Hanselmann
      http.HTTP_SERVER: http.HTTP_GANETI_VERSION,
375 02cab3e7 Michael Hanselmann
      })
376 02cab3e7 Michael Hanselmann
377 02cab3e7 Michael Hanselmann
    # Get response reason based on code
378 02cab3e7 Michael Hanselmann
    response_code = self.response_msg.start_line.code
379 02cab3e7 Michael Hanselmann
    if response_code in self.responses:
380 02cab3e7 Michael Hanselmann
      response_reason = self.responses[response_code][0]
381 02cab3e7 Michael Hanselmann
    else:
382 02cab3e7 Michael Hanselmann
      response_reason = ""
383 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.reason = response_reason
384 02cab3e7 Michael Hanselmann
385 02cab3e7 Michael Hanselmann
    logging.info("%s:%s %s %s", self.client_addr[0], self.client_addr[1],
386 02cab3e7 Michael Hanselmann
                 self.request_msg.start_line, response_code)
387 02cab3e7 Michael Hanselmann
388 02cab3e7 Michael Hanselmann
    try:
389 02cab3e7 Michael Hanselmann
      _HttpServerToClientMessageWriter(self.sock, self.request_msg,
390 02cab3e7 Michael Hanselmann
                                       self.response_msg, self.WRITE_TIMEOUT)
391 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
392 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending response")
393 02cab3e7 Michael Hanselmann
    except socket.error, err:
394 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending response: %s" % err)
395 02cab3e7 Michael Hanselmann
396 02cab3e7 Michael Hanselmann
  def _SetErrorStatus(self, err):
397 02cab3e7 Michael Hanselmann
    """Sets the response code and body from a HttpException.
398 02cab3e7 Michael Hanselmann

399 02cab3e7 Michael Hanselmann
    @type err: HttpException
400 02cab3e7 Michael Hanselmann
    @param err: Exception instance
401 02cab3e7 Michael Hanselmann

402 02cab3e7 Michael Hanselmann
    """
403 02cab3e7 Michael Hanselmann
    try:
404 02cab3e7 Michael Hanselmann
      (shortmsg, longmsg) = self.responses[err.code]
405 02cab3e7 Michael Hanselmann
    except KeyError:
406 02cab3e7 Michael Hanselmann
      shortmsg = longmsg = "Unknown"
407 02cab3e7 Michael Hanselmann
408 02cab3e7 Michael Hanselmann
    if err.message:
409 02cab3e7 Michael Hanselmann
      message = err.message
410 02cab3e7 Michael Hanselmann
    else:
411 02cab3e7 Michael Hanselmann
      message = shortmsg
412 02cab3e7 Michael Hanselmann
413 02cab3e7 Michael Hanselmann
    values = {
414 02cab3e7 Michael Hanselmann
      "code": err.code,
415 02cab3e7 Michael Hanselmann
      "message": cgi.escape(message),
416 02cab3e7 Michael Hanselmann
      "explain": longmsg,
417 02cab3e7 Michael Hanselmann
      }
418 02cab3e7 Michael Hanselmann
419 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.code = err.code
420 a8e01e9f Michael Hanselmann
421 a8e01e9f Michael Hanselmann
    headers = {}
422 a8e01e9f Michael Hanselmann
    if err.headers:
423 a8e01e9f Michael Hanselmann
      headers.update(err.headers)
424 a8e01e9f Michael Hanselmann
    headers[http.HTTP_CONTENT_TYPE] = self.error_content_type
425 a8e01e9f Michael Hanselmann
    self.response_msg.headers = headers
426 a8e01e9f Michael Hanselmann
427 1f8588f6 Iustin Pop
    self.response_msg.body = self._FormatErrorMessage(values)
428 02cab3e7 Michael Hanselmann
429 1f8588f6 Iustin Pop
  def _FormatErrorMessage(self, values):
430 1f8588f6 Iustin Pop
    """Formats the body of an error message.
431 1f8588f6 Iustin Pop

432 1f8588f6 Iustin Pop
    @type values: dict
433 1f8588f6 Iustin Pop
    @param values: dictionary with keys code, message and explain.
434 1f8588f6 Iustin Pop
    @rtype: string
435 1f8588f6 Iustin Pop
    @return: the body of the message
436 1f8588f6 Iustin Pop

437 1f8588f6 Iustin Pop
    """
438 1f8588f6 Iustin Pop
    return self.error_message_format % values
439 02cab3e7 Michael Hanselmann
440 57fd6d0b Michael Hanselmann
441 112d240d Guido Trotter
class HttpServer(http.HttpBase, asyncore.dispatcher):
442 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
443 02cab3e7 Michael Hanselmann

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

446 02cab3e7 Michael Hanselmann
  """
447 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
448 02cab3e7 Michael Hanselmann
449 02cab3e7 Michael Hanselmann
  def __init__(self, mainloop, local_address, port,
450 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
451 1f8588f6 Iustin Pop
               request_executor_class=None):
452 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
453 02cab3e7 Michael Hanselmann

454 02cab3e7 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
455 02cab3e7 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
456 c41eea6e Iustin Pop
    @type local_address: string
457 02cab3e7 Michael Hanselmann
    @param local_address: Local IP address to bind to
458 02cab3e7 Michael Hanselmann
    @type port: int
459 02cab3e7 Michael Hanselmann
    @param port: TCP port to listen on
460 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
461 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
462 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
463 25e7b43f Iustin Pop
    @param ssl_verify_peer: Whether to require client certificate
464 25e7b43f Iustin Pop
        and compare it with our certificate
465 1f8588f6 Iustin Pop
    @type request_executor_class: class
466 1f8588f6 Iustin Pop
    @param request_executor_class: an class derived from the
467 1f8588f6 Iustin Pop
        HttpServerRequestExecutor class
468 02cab3e7 Michael Hanselmann

469 02cab3e7 Michael Hanselmann
    """
470 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
471 112d240d Guido Trotter
    asyncore.dispatcher.__init__(self)
472 02cab3e7 Michael Hanselmann
473 1f8588f6 Iustin Pop
    if request_executor_class is None:
474 1f8588f6 Iustin Pop
      self.request_executor = HttpServerRequestExecutor
475 1f8588f6 Iustin Pop
    else:
476 1f8588f6 Iustin Pop
      self.request_executor = request_executor_class
477 1f8588f6 Iustin Pop
478 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
479 02cab3e7 Michael Hanselmann
    self.local_address = local_address
480 02cab3e7 Michael Hanselmann
    self.port = port
481 02cab3e7 Michael Hanselmann
482 02cab3e7 Michael Hanselmann
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer)
483 02cab3e7 Michael Hanselmann
484 02cab3e7 Michael Hanselmann
    # Allow port to be reused
485 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
486 02cab3e7 Michael Hanselmann
487 02cab3e7 Michael Hanselmann
    self._children = []
488 112d240d Guido Trotter
    self.set_socket(self.socket)
489 112d240d Guido Trotter
    self.accepting = True
490 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
491 02cab3e7 Michael Hanselmann
492 02cab3e7 Michael Hanselmann
  def Start(self):
493 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
494 59305197 Michael Hanselmann
    self.socket.listen(1024)
495 02cab3e7 Michael Hanselmann
496 02cab3e7 Michael Hanselmann
  def Stop(self):
497 02cab3e7 Michael Hanselmann
    self.socket.close()
498 02cab3e7 Michael Hanselmann
499 112d240d Guido Trotter
  def handle_accept(self):
500 112d240d Guido Trotter
    self._IncomingConnection()
501 02cab3e7 Michael Hanselmann
502 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
503 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
504 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
505 02cab3e7 Michael Hanselmann
506 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
507 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
508 02cab3e7 Michael Hanselmann

509 02cab3e7 Michael Hanselmann
    @type quick: bool
510 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
511 02cab3e7 Michael Hanselmann

512 02cab3e7 Michael Hanselmann
    """
513 02cab3e7 Michael Hanselmann
    if not quick:
514 02cab3e7 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
515 02cab3e7 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
516 02cab3e7 Michael Hanselmann
        try:
517 02cab3e7 Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
518 02cab3e7 Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
519 02cab3e7 Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
520 02cab3e7 Michael Hanselmann
          # and killing them after some time.
521 7c4d6c7b Michael Hanselmann
          pid, _ = os.waitpid(0, 0)
522 02cab3e7 Michael Hanselmann
        except os.error:
523 02cab3e7 Michael Hanselmann
          pid = None
524 02cab3e7 Michael Hanselmann
        if pid and pid in self._children:
525 02cab3e7 Michael Hanselmann
          self._children.remove(pid)
526 02cab3e7 Michael Hanselmann
527 02cab3e7 Michael Hanselmann
    for child in self._children:
528 02cab3e7 Michael Hanselmann
      try:
529 1122eb25 Iustin Pop
        pid, _ = os.waitpid(child, os.WNOHANG)
530 02cab3e7 Michael Hanselmann
      except os.error:
531 02cab3e7 Michael Hanselmann
        pid = None
532 02cab3e7 Michael Hanselmann
      if pid and pid in self._children:
533 02cab3e7 Michael Hanselmann
        self._children.remove(pid)
534 02cab3e7 Michael Hanselmann
535 02cab3e7 Michael Hanselmann
  def _IncomingConnection(self):
536 02cab3e7 Michael Hanselmann
    """Called for each incoming connection
537 02cab3e7 Michael Hanselmann

538 02cab3e7 Michael Hanselmann
    """
539 7260cfbe Iustin Pop
    # pylint: disable-msg=W0212
540 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
541 02cab3e7 Michael Hanselmann
542 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
543 02cab3e7 Michael Hanselmann
544 02cab3e7 Michael Hanselmann
    pid = os.fork()
545 02cab3e7 Michael Hanselmann
    if pid == 0:
546 02cab3e7 Michael Hanselmann
      # Child process
547 02cab3e7 Michael Hanselmann
      try:
548 bcb1a39e Michael Hanselmann
        # The client shouldn't keep the listening socket open. If the parent
549 bcb1a39e Michael Hanselmann
        # process is restarted, it would fail when there's already something
550 bcb1a39e Michael Hanselmann
        # listening (in this case its own child from a previous run) on the
551 bcb1a39e Michael Hanselmann
        # same port.
552 bcb1a39e Michael Hanselmann
        try:
553 bcb1a39e Michael Hanselmann
          self.socket.close()
554 bcb1a39e Michael Hanselmann
        except socket.error:
555 bcb1a39e Michael Hanselmann
          pass
556 bcb1a39e Michael Hanselmann
        self.socket = None
557 bcb1a39e Michael Hanselmann
558 82869978 Michael Hanselmann
        # In case the handler code uses temporary files
559 82869978 Michael Hanselmann
        utils.ResetTempfileModule()
560 82869978 Michael Hanselmann
561 1f8588f6 Iustin Pop
        self.request_executor(self, connection, client_addr)
562 7260cfbe Iustin Pop
      except Exception: # pylint: disable-msg=W0703
563 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
564 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
565 02cab3e7 Michael Hanselmann
        os._exit(1)
566 02cab3e7 Michael Hanselmann
      os._exit(0)
567 02cab3e7 Michael Hanselmann
    else:
568 02cab3e7 Michael Hanselmann
      self._children.append(pid)
569 02cab3e7 Michael Hanselmann
570 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
571 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
572 f8bd7df3 Michael Hanselmann

573 5bbd3f7f Michael Hanselmann
    Can be overridden by a subclass.
574 f8bd7df3 Michael Hanselmann

575 f8bd7df3 Michael Hanselmann
    """
576 f8bd7df3 Michael Hanselmann
577 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
578 02cab3e7 Michael Hanselmann
    """Handles a request.
579 02cab3e7 Michael Hanselmann

580 5bbd3f7f Michael Hanselmann
    Must be overridden by subclass.
581 02cab3e7 Michael Hanselmann

582 02cab3e7 Michael Hanselmann
    """
583 02cab3e7 Michael Hanselmann
    raise NotImplementedError()