Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ 377ae13e

History | View | Annotate | Download (17.8 kB)

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

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

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

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

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

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

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

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

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

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

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

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

220 c81f452f Michael Hanselmann
  """
221 c81f452f Michael Hanselmann
  handler_context = _HttpServerRequest(req_msg.start_line.method,
222 c81f452f Michael Hanselmann
                                       req_msg.start_line.path,
223 c81f452f Michael Hanselmann
                                       req_msg.headers,
224 c81f452f Michael Hanselmann
                                       req_msg.body)
225 c81f452f Michael Hanselmann
226 c81f452f Michael Hanselmann
  logging.debug("Handling request %r", handler_context)
227 c81f452f Michael Hanselmann
228 c81f452f Michael Hanselmann
  try:
229 c81f452f Michael Hanselmann
    try:
230 c81f452f Michael Hanselmann
      # Authentication, etc.
231 c81f452f Michael Hanselmann
      handler.PreHandleRequest(handler_context)
232 c81f452f Michael Hanselmann
233 c81f452f Michael Hanselmann
      # Call actual request handler
234 c81f452f Michael Hanselmann
      result = handler.HandleRequest(handler_context)
235 c81f452f Michael Hanselmann
    except (http.HttpException, KeyboardInterrupt, SystemExit):
236 c81f452f Michael Hanselmann
      raise
237 c81f452f Michael Hanselmann
    except Exception, err:
238 c81f452f Michael Hanselmann
      logging.exception("Caught exception")
239 c81f452f Michael Hanselmann
      raise http.HttpInternalServerError(message=str(err))
240 c81f452f Michael Hanselmann
    except:
241 c81f452f Michael Hanselmann
      logging.exception("Unknown exception")
242 c81f452f Michael Hanselmann
      raise http.HttpInternalServerError(message="Unknown error")
243 c81f452f Michael Hanselmann
244 c81f452f Michael Hanselmann
    if not isinstance(result, basestring):
245 c81f452f Michael Hanselmann
      raise http.HttpError("Handler function didn't return string type")
246 c81f452f Michael Hanselmann
247 c81f452f Michael Hanselmann
    return (http.HTTP_OK, handler_context.resp_headers, result)
248 c81f452f Michael Hanselmann
  finally:
249 c81f452f Michael Hanselmann
    # No reason to keep this any longer, even for exceptions
250 c81f452f Michael Hanselmann
    handler_context.private = None
251 c81f452f Michael Hanselmann
252 c81f452f Michael Hanselmann
253 1f8588f6 Iustin Pop
class HttpServerRequestExecutor(object):
254 02cab3e7 Michael Hanselmann
  """Implements server side of HTTP.
255 02cab3e7 Michael Hanselmann

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

261 02cab3e7 Michael Hanselmann
  """
262 02cab3e7 Michael Hanselmann
  # The default request version.  This only affects responses up until
263 02cab3e7 Michael Hanselmann
  # the point where the request line is parsed, so it mainly decides what
264 02cab3e7 Michael Hanselmann
  # the client gets back when sending a malformed request line.
265 02cab3e7 Michael Hanselmann
  # Most web servers default to HTTP 0.9, i.e. don't send a status line.
266 02cab3e7 Michael Hanselmann
  default_request_version = http.HTTP_0_9
267 02cab3e7 Michael Hanselmann
268 02cab3e7 Michael Hanselmann
  responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
269 02cab3e7 Michael Hanselmann
270 02cab3e7 Michael Hanselmann
  # Timeouts in seconds for socket layer
271 02cab3e7 Michael Hanselmann
  WRITE_TIMEOUT = 10
272 02cab3e7 Michael Hanselmann
  READ_TIMEOUT = 10
273 02cab3e7 Michael Hanselmann
  CLOSE_TIMEOUT = 1
274 02cab3e7 Michael Hanselmann
275 e0003509 Michael Hanselmann
  def __init__(self, server, handler, sock, client_addr):
276 02cab3e7 Michael Hanselmann
    """Initializes this class.
277 02cab3e7 Michael Hanselmann

278 02cab3e7 Michael Hanselmann
    """
279 02cab3e7 Michael Hanselmann
    self.server = server
280 e0003509 Michael Hanselmann
    self.handler = handler
281 02cab3e7 Michael Hanselmann
    self.sock = sock
282 02cab3e7 Michael Hanselmann
    self.client_addr = client_addr
283 02cab3e7 Michael Hanselmann
284 02cab3e7 Michael Hanselmann
    self.request_msg = http.HttpMessage()
285 02cab3e7 Michael Hanselmann
    self.response_msg = http.HttpMessage()
286 02cab3e7 Michael Hanselmann
287 02cab3e7 Michael Hanselmann
    self.response_msg.start_line = \
288 02cab3e7 Michael Hanselmann
      http.HttpServerToClientStartLine(version=self.default_request_version,
289 02cab3e7 Michael Hanselmann
                                       code=None, reason=None)
290 02cab3e7 Michael Hanselmann
291 02cab3e7 Michael Hanselmann
    # Disable Python's timeout
292 02cab3e7 Michael Hanselmann
    self.sock.settimeout(None)
293 02cab3e7 Michael Hanselmann
294 02cab3e7 Michael Hanselmann
    # Operate in non-blocking mode
295 02cab3e7 Michael Hanselmann
    self.sock.setblocking(0)
296 02cab3e7 Michael Hanselmann
297 14d57a8b Iustin Pop
    logging.debug("Connection from %s:%s", client_addr[0], client_addr[1])
298 02cab3e7 Michael Hanselmann
    try:
299 02cab3e7 Michael Hanselmann
      request_msg_reader = None
300 02cab3e7 Michael Hanselmann
      force_close = True
301 02cab3e7 Michael Hanselmann
      try:
302 f2e13d55 Michael Hanselmann
        # Do the secret SSL handshake
303 f2e13d55 Michael Hanselmann
        if self.server.using_ssl:
304 f2e13d55 Michael Hanselmann
          self.sock.set_accept_state()
305 f2e13d55 Michael Hanselmann
          try:
306 aea0ed67 Michael Hanselmann
            http.Handshake(self.sock, self.WRITE_TIMEOUT)
307 f2e13d55 Michael Hanselmann
          except http.HttpSessionHandshakeUnexpectedEOF:
308 f2e13d55 Michael Hanselmann
            # Ignore rest
309 f2e13d55 Michael Hanselmann
            return
310 f2e13d55 Michael Hanselmann
311 02cab3e7 Michael Hanselmann
        try:
312 02cab3e7 Michael Hanselmann
          try:
313 02cab3e7 Michael Hanselmann
            request_msg_reader = self._ReadRequest()
314 200e38ac Michael Hanselmann
315 200e38ac Michael Hanselmann
            # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond
316 200e38ac Michael Hanselmann
            # with a 400 (Bad Request) status code to any HTTP/1.1 request
317 200e38ac Michael Hanselmann
            # message which lacks a Host header field.
318 200e38ac Michael Hanselmann
            if (self.request_msg.start_line.version == http.HTTP_1_1 and
319 200e38ac Michael Hanselmann
                http.HTTP_HOST not in self.request_msg.headers):
320 200e38ac Michael Hanselmann
              raise http.HttpBadRequest(message="Missing Host header")
321 200e38ac Michael Hanselmann
322 c81f452f Michael Hanselmann
            (self.response_msg.start_line.code, self.response_msg.headers,
323 c81f452f Michael Hanselmann
             self.response_msg.body) = \
324 e0003509 Michael Hanselmann
              HandleServerRequest(self.handler, self.request_msg)
325 02cab3e7 Michael Hanselmann
326 02cab3e7 Michael Hanselmann
            # Only wait for client to close if we didn't have any exception.
327 02cab3e7 Michael Hanselmann
            force_close = False
328 02cab3e7 Michael Hanselmann
          except http.HttpException, err:
329 02cab3e7 Michael Hanselmann
            self._SetErrorStatus(err)
330 02cab3e7 Michael Hanselmann
        finally:
331 02cab3e7 Michael Hanselmann
          # Try to send a response
332 02cab3e7 Michael Hanselmann
          self._SendResponse()
333 02cab3e7 Michael Hanselmann
      finally:
334 aea0ed67 Michael Hanselmann
        http.ShutdownConnection(sock, self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT,
335 02cab3e7 Michael Hanselmann
                                request_msg_reader, force_close)
336 02cab3e7 Michael Hanselmann
337 02cab3e7 Michael Hanselmann
      self.sock.close()
338 02cab3e7 Michael Hanselmann
      self.sock = None
339 02cab3e7 Michael Hanselmann
    finally:
340 14d57a8b Iustin Pop
      logging.debug("Disconnected %s:%s", client_addr[0], client_addr[1])
341 02cab3e7 Michael Hanselmann
342 02cab3e7 Michael Hanselmann
  def _ReadRequest(self):
343 02cab3e7 Michael Hanselmann
    """Reads a request sent by client.
344 02cab3e7 Michael Hanselmann

345 02cab3e7 Michael Hanselmann
    """
346 02cab3e7 Michael Hanselmann
    try:
347 02cab3e7 Michael Hanselmann
      request_msg_reader = \
348 02cab3e7 Michael Hanselmann
        _HttpClientToServerMessageReader(self.sock, self.request_msg,
349 02cab3e7 Michael Hanselmann
                                         self.READ_TIMEOUT)
350 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
351 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading request")
352 02cab3e7 Michael Hanselmann
    except socket.error, err:
353 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading request: %s" % err)
354 02cab3e7 Michael Hanselmann
355 02cab3e7 Michael Hanselmann
    self.response_msg.start_line.version = self.request_msg.start_line.version
356 02cab3e7 Michael Hanselmann
357 02cab3e7 Michael Hanselmann
    return request_msg_reader
358 02cab3e7 Michael Hanselmann
359 02cab3e7 Michael Hanselmann
  def _SendResponse(self):
360 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
361 02cab3e7 Michael Hanselmann

362 02cab3e7 Michael Hanselmann
    """
363 e0003509 Michael Hanselmann
    # HttpMessage.start_line can be of different types, pylint: disable=E1103
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 377ae13e Michael Hanselmann
    (content_type, body) = self.handler.FormatErrorMessage(values)
420 377ae13e Michael Hanselmann
421 377ae13e Michael Hanselmann
    headers = {
422 377ae13e Michael Hanselmann
      http.HTTP_CONTENT_TYPE: content_type,
423 377ae13e Michael Hanselmann
      }
424 a8e01e9f Michael Hanselmann
425 a8e01e9f Michael Hanselmann
    if err.headers:
426 a8e01e9f Michael Hanselmann
      headers.update(err.headers)
427 1f8588f6 Iustin Pop
428 377ae13e Michael Hanselmann
    self.response_msg.start_line.code = err.code
429 377ae13e Michael Hanselmann
    self.response_msg.headers = headers
430 377ae13e Michael Hanselmann
    self.response_msg.body = body
431 02cab3e7 Michael Hanselmann
432 57fd6d0b Michael Hanselmann
433 112d240d Guido Trotter
class HttpServer(http.HttpBase, asyncore.dispatcher):
434 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
435 02cab3e7 Michael Hanselmann

436 02cab3e7 Michael Hanselmann
  """
437 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
438 02cab3e7 Michael Hanselmann
439 e0003509 Michael Hanselmann
  def __init__(self, mainloop, local_address, port, handler,
440 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
441 1f8588f6 Iustin Pop
               request_executor_class=None):
442 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
443 02cab3e7 Michael Hanselmann

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

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

500 02cab3e7 Michael Hanselmann
    @type quick: bool
501 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
502 02cab3e7 Michael Hanselmann

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

529 02cab3e7 Michael Hanselmann
    """
530 b459a848 Andrea Spadaccini
    # pylint: disable=W0212
531 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
532 02cab3e7 Michael Hanselmann
533 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
534 02cab3e7 Michael Hanselmann
535 02cab3e7 Michael Hanselmann
    pid = os.fork()
536 02cab3e7 Michael Hanselmann
    if pid == 0:
537 02cab3e7 Michael Hanselmann
      # Child process
538 02cab3e7 Michael Hanselmann
      try:
539 bcb1a39e Michael Hanselmann
        # The client shouldn't keep the listening socket open. If the parent
540 bcb1a39e Michael Hanselmann
        # process is restarted, it would fail when there's already something
541 bcb1a39e Michael Hanselmann
        # listening (in this case its own child from a previous run) on the
542 bcb1a39e Michael Hanselmann
        # same port.
543 bcb1a39e Michael Hanselmann
        try:
544 bcb1a39e Michael Hanselmann
          self.socket.close()
545 bcb1a39e Michael Hanselmann
        except socket.error:
546 bcb1a39e Michael Hanselmann
          pass
547 bcb1a39e Michael Hanselmann
        self.socket = None
548 bcb1a39e Michael Hanselmann
549 82869978 Michael Hanselmann
        # In case the handler code uses temporary files
550 82869978 Michael Hanselmann
        utils.ResetTempfileModule()
551 82869978 Michael Hanselmann
552 e0003509 Michael Hanselmann
        self.request_executor(self, self.handler, connection, client_addr)
553 b459a848 Andrea Spadaccini
      except Exception: # pylint: disable=W0703
554 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
555 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
556 02cab3e7 Michael Hanselmann
        os._exit(1)
557 02cab3e7 Michael Hanselmann
      os._exit(0)
558 02cab3e7 Michael Hanselmann
    else:
559 02cab3e7 Michael Hanselmann
      self._children.append(pid)
560 02cab3e7 Michael Hanselmann
561 e0003509 Michael Hanselmann
562 e0003509 Michael Hanselmann
class HttpServerHandler(object):
563 e0003509 Michael Hanselmann
  """Base class for handling HTTP server requests.
564 e0003509 Michael Hanselmann

565 e0003509 Michael Hanselmann
  Users of this class must subclass it and override the L{HandleRequest}
566 e0003509 Michael Hanselmann
  function.
567 e0003509 Michael Hanselmann

568 e0003509 Michael Hanselmann
  """
569 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
570 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
571 f8bd7df3 Michael Hanselmann

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

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

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

581 02cab3e7 Michael Hanselmann
    """
582 02cab3e7 Michael Hanselmann
    raise NotImplementedError()
583 377ae13e Michael Hanselmann
584 377ae13e Michael Hanselmann
  @staticmethod
585 377ae13e Michael Hanselmann
  def FormatErrorMessage(values):
586 377ae13e Michael Hanselmann
    """Formats the body of an error message.
587 377ae13e Michael Hanselmann

588 377ae13e Michael Hanselmann
    @type values: dict
589 377ae13e Michael Hanselmann
    @param values: dictionary with keys C{code}, C{message} and C{explain}.
590 377ae13e Michael Hanselmann
    @rtype: tuple; (string, string)
591 377ae13e Michael Hanselmann
    @return: Content-type and response body
592 377ae13e Michael Hanselmann

593 377ae13e Michael Hanselmann
    """
594 377ae13e Michael Hanselmann
    return (DEFAULT_ERROR_CONTENT_TYPE, DEFAULT_ERROR_MESSAGE % values)