Statistics
| Branch: | Tag: | Revision:

root / lib / http / server.py @ 57a6042e

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

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

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

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

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

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

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

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

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

168 02cab3e7 Michael Hanselmann
    Example: "GET /index.html HTTP/1.1"
169 02cab3e7 Michael Hanselmann

170 02cab3e7 Michael Hanselmann
    @type start_line: string
171 02cab3e7 Michael Hanselmann
    @param start_line: Start line
172 02cab3e7 Michael Hanselmann

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

222 c81f452f Michael Hanselmann
  """
223 c81f452f Michael Hanselmann
  handler_context = _HttpServerRequest(req_msg.start_line.method,
224 c81f452f Michael Hanselmann
                                       req_msg.start_line.path,
225 c81f452f Michael Hanselmann
                                       req_msg.headers,
226 c81f452f Michael Hanselmann
                                       req_msg.body)
227 c81f452f Michael Hanselmann
228 c81f452f Michael Hanselmann
  logging.debug("Handling request %r", handler_context)
229 c81f452f Michael Hanselmann
230 c81f452f Michael Hanselmann
  try:
231 c81f452f Michael Hanselmann
    try:
232 c81f452f Michael Hanselmann
      # Authentication, etc.
233 c81f452f Michael Hanselmann
      handler.PreHandleRequest(handler_context)
234 c81f452f Michael Hanselmann
235 c81f452f Michael Hanselmann
      # Call actual request handler
236 c81f452f Michael Hanselmann
      result = handler.HandleRequest(handler_context)
237 352e1a26 Michael Hanselmann
    except (http.HttpException, errors.RapiTestResult,
238 352e1a26 Michael Hanselmann
            KeyboardInterrupt, SystemExit):
239 c81f452f Michael Hanselmann
      raise
240 c81f452f Michael Hanselmann
    except Exception, err:
241 c81f452f Michael Hanselmann
      logging.exception("Caught exception")
242 c81f452f Michael Hanselmann
      raise http.HttpInternalServerError(message=str(err))
243 c81f452f Michael Hanselmann
    except:
244 c81f452f Michael Hanselmann
      logging.exception("Unknown exception")
245 c81f452f Michael Hanselmann
      raise http.HttpInternalServerError(message="Unknown error")
246 c81f452f Michael Hanselmann
247 c81f452f Michael Hanselmann
    if not isinstance(result, basestring):
248 c81f452f Michael Hanselmann
      raise http.HttpError("Handler function didn't return string type")
249 c81f452f Michael Hanselmann
250 c81f452f Michael Hanselmann
    return (http.HTTP_OK, handler_context.resp_headers, result)
251 c81f452f Michael Hanselmann
  finally:
252 c81f452f Michael Hanselmann
    # No reason to keep this any longer, even for exceptions
253 c81f452f Michael Hanselmann
    handler_context.private = None
254 c81f452f Michael Hanselmann
255 c81f452f Michael Hanselmann
256 d859a2cf Michael Hanselmann
class HttpResponder(object):
257 d859a2cf Michael Hanselmann
  # The default request version.  This only affects responses up until
258 d859a2cf Michael Hanselmann
  # the point where the request line is parsed, so it mainly decides what
259 d859a2cf Michael Hanselmann
  # the client gets back when sending a malformed request line.
260 d859a2cf Michael Hanselmann
  # Most web servers default to HTTP 0.9, i.e. don't send a status line.
261 d859a2cf Michael Hanselmann
  default_request_version = http.HTTP_0_9
262 d859a2cf Michael Hanselmann
263 d859a2cf Michael Hanselmann
  responses = BaseHTTPServer.BaseHTTPRequestHandler.responses
264 d859a2cf Michael Hanselmann
265 d859a2cf Michael Hanselmann
  def __init__(self, handler):
266 d859a2cf Michael Hanselmann
    """Initializes this class.
267 d859a2cf Michael Hanselmann

268 d859a2cf Michael Hanselmann
    """
269 d859a2cf Michael Hanselmann
    self._handler = handler
270 d859a2cf Michael Hanselmann
271 d859a2cf Michael Hanselmann
  def __call__(self, fn):
272 d859a2cf Michael Hanselmann
    """Handles a request.
273 d859a2cf Michael Hanselmann

274 d859a2cf Michael Hanselmann
    @type fn: callable
275 d859a2cf Michael Hanselmann
    @param fn: Callback for retrieving HTTP request, must return a tuple
276 d859a2cf Michael Hanselmann
      containing request message (L{http.HttpMessage}) and C{None} or the
277 d859a2cf Michael Hanselmann
      message reader (L{_HttpClientToServerMessageReader})
278 d859a2cf Michael Hanselmann

279 d859a2cf Michael Hanselmann
    """
280 d859a2cf Michael Hanselmann
    response_msg = http.HttpMessage()
281 d859a2cf Michael Hanselmann
    response_msg.start_line = \
282 d859a2cf Michael Hanselmann
      http.HttpServerToClientStartLine(version=self.default_request_version,
283 d859a2cf Michael Hanselmann
                                       code=None, reason=None)
284 d859a2cf Michael Hanselmann
285 d859a2cf Michael Hanselmann
    force_close = True
286 d859a2cf Michael Hanselmann
287 d859a2cf Michael Hanselmann
    try:
288 d859a2cf Michael Hanselmann
      (request_msg, req_msg_reader) = fn()
289 d859a2cf Michael Hanselmann
290 d859a2cf Michael Hanselmann
      response_msg.start_line.version = request_msg.start_line.version
291 d859a2cf Michael Hanselmann
292 d859a2cf Michael Hanselmann
      # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond
293 d859a2cf Michael Hanselmann
      # with a 400 (Bad Request) status code to any HTTP/1.1 request
294 d859a2cf Michael Hanselmann
      # message which lacks a Host header field.
295 d859a2cf Michael Hanselmann
      if (request_msg.start_line.version == http.HTTP_1_1 and
296 d859a2cf Michael Hanselmann
          not (request_msg.headers and
297 d859a2cf Michael Hanselmann
               http.HTTP_HOST in request_msg.headers)):
298 d859a2cf Michael Hanselmann
        raise http.HttpBadRequest(message="Missing Host header")
299 d859a2cf Michael Hanselmann
300 d859a2cf Michael Hanselmann
      (response_msg.start_line.code, response_msg.headers,
301 d859a2cf Michael Hanselmann
       response_msg.body) = \
302 d859a2cf Michael Hanselmann
        _HandleServerRequestInner(self._handler, request_msg)
303 d859a2cf Michael Hanselmann
    except http.HttpException, err:
304 d859a2cf Michael Hanselmann
      self._SetError(self.responses, self._handler, response_msg, err)
305 d859a2cf Michael Hanselmann
    else:
306 d859a2cf Michael Hanselmann
      # Only wait for client to close if we didn't have any exception.
307 d859a2cf Michael Hanselmann
      force_close = False
308 d859a2cf Michael Hanselmann
309 d859a2cf Michael Hanselmann
    return (request_msg, req_msg_reader, force_close,
310 d859a2cf Michael Hanselmann
            self._Finalize(self.responses, response_msg))
311 d859a2cf Michael Hanselmann
312 d859a2cf Michael Hanselmann
  @staticmethod
313 d859a2cf Michael Hanselmann
  def _SetError(responses, handler, response_msg, err):
314 d859a2cf Michael Hanselmann
    """Sets the response code and body from a HttpException.
315 d859a2cf Michael Hanselmann

316 d859a2cf Michael Hanselmann
    @type err: HttpException
317 d859a2cf Michael Hanselmann
    @param err: Exception instance
318 d859a2cf Michael Hanselmann

319 d859a2cf Michael Hanselmann
    """
320 d859a2cf Michael Hanselmann
    try:
321 d859a2cf Michael Hanselmann
      (shortmsg, longmsg) = responses[err.code]
322 d859a2cf Michael Hanselmann
    except KeyError:
323 d859a2cf Michael Hanselmann
      shortmsg = longmsg = "Unknown"
324 d859a2cf Michael Hanselmann
325 d859a2cf Michael Hanselmann
    if err.message:
326 d859a2cf Michael Hanselmann
      message = err.message
327 d859a2cf Michael Hanselmann
    else:
328 d859a2cf Michael Hanselmann
      message = shortmsg
329 d859a2cf Michael Hanselmann
330 d859a2cf Michael Hanselmann
    values = {
331 d859a2cf Michael Hanselmann
      "code": err.code,
332 d859a2cf Michael Hanselmann
      "message": cgi.escape(message),
333 d859a2cf Michael Hanselmann
      "explain": longmsg,
334 d859a2cf Michael Hanselmann
      }
335 d859a2cf Michael Hanselmann
336 d859a2cf Michael Hanselmann
    (content_type, body) = handler.FormatErrorMessage(values)
337 d859a2cf Michael Hanselmann
338 d859a2cf Michael Hanselmann
    headers = {
339 d859a2cf Michael Hanselmann
      http.HTTP_CONTENT_TYPE: content_type,
340 d859a2cf Michael Hanselmann
      }
341 d859a2cf Michael Hanselmann
342 d859a2cf Michael Hanselmann
    if err.headers:
343 d859a2cf Michael Hanselmann
      headers.update(err.headers)
344 d859a2cf Michael Hanselmann
345 d859a2cf Michael Hanselmann
    response_msg.start_line.code = err.code
346 d859a2cf Michael Hanselmann
    response_msg.headers = headers
347 d859a2cf Michael Hanselmann
    response_msg.body = body
348 d859a2cf Michael Hanselmann
349 d859a2cf Michael Hanselmann
  @staticmethod
350 d859a2cf Michael Hanselmann
  def _Finalize(responses, msg):
351 d859a2cf Michael Hanselmann
    assert msg.start_line.reason is None
352 d859a2cf Michael Hanselmann
353 d859a2cf Michael Hanselmann
    if not msg.headers:
354 d859a2cf Michael Hanselmann
      msg.headers = {}
355 d859a2cf Michael Hanselmann
356 d859a2cf Michael Hanselmann
    msg.headers.update({
357 d859a2cf Michael Hanselmann
      # TODO: Keep-alive is not supported
358 d859a2cf Michael Hanselmann
      http.HTTP_CONNECTION: "close",
359 d859a2cf Michael Hanselmann
      http.HTTP_DATE: _DateTimeHeader(),
360 d859a2cf Michael Hanselmann
      http.HTTP_SERVER: http.HTTP_GANETI_VERSION,
361 d859a2cf Michael Hanselmann
      })
362 d859a2cf Michael Hanselmann
363 d859a2cf Michael Hanselmann
    # Get response reason based on code
364 d859a2cf Michael Hanselmann
    try:
365 d859a2cf Michael Hanselmann
      code_desc = responses[msg.start_line.code]
366 d859a2cf Michael Hanselmann
    except KeyError:
367 d859a2cf Michael Hanselmann
      reason = ""
368 d859a2cf Michael Hanselmann
    else:
369 d859a2cf Michael Hanselmann
      (reason, _) = code_desc
370 d859a2cf Michael Hanselmann
371 d859a2cf Michael Hanselmann
    msg.start_line.reason = reason
372 d859a2cf Michael Hanselmann
373 d859a2cf Michael Hanselmann
    return msg
374 d859a2cf Michael Hanselmann
375 d859a2cf Michael Hanselmann
376 1f8588f6 Iustin Pop
class HttpServerRequestExecutor(object):
377 02cab3e7 Michael Hanselmann
  """Implements server side of HTTP.
378 02cab3e7 Michael Hanselmann

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

384 02cab3e7 Michael Hanselmann
  """
385 02cab3e7 Michael Hanselmann
  # Timeouts in seconds for socket layer
386 02cab3e7 Michael Hanselmann
  WRITE_TIMEOUT = 10
387 02cab3e7 Michael Hanselmann
  READ_TIMEOUT = 10
388 02cab3e7 Michael Hanselmann
  CLOSE_TIMEOUT = 1
389 02cab3e7 Michael Hanselmann
390 e0003509 Michael Hanselmann
  def __init__(self, server, handler, sock, client_addr):
391 02cab3e7 Michael Hanselmann
    """Initializes this class.
392 02cab3e7 Michael Hanselmann

393 02cab3e7 Michael Hanselmann
    """
394 d859a2cf Michael Hanselmann
    responder = HttpResponder(handler)
395 02cab3e7 Michael Hanselmann
396 02cab3e7 Michael Hanselmann
    # Disable Python's timeout
397 d859a2cf Michael Hanselmann
    sock.settimeout(None)
398 02cab3e7 Michael Hanselmann
399 02cab3e7 Michael Hanselmann
    # Operate in non-blocking mode
400 d859a2cf Michael Hanselmann
    sock.setblocking(0)
401 d859a2cf Michael Hanselmann
402 d859a2cf Michael Hanselmann
    request_msg_reader = None
403 d859a2cf Michael Hanselmann
    force_close = True
404 02cab3e7 Michael Hanselmann
405 14d57a8b Iustin Pop
    logging.debug("Connection from %s:%s", client_addr[0], client_addr[1])
406 02cab3e7 Michael Hanselmann
    try:
407 d859a2cf Michael Hanselmann
      # Block for closing connection
408 02cab3e7 Michael Hanselmann
      try:
409 f2e13d55 Michael Hanselmann
        # Do the secret SSL handshake
410 d859a2cf Michael Hanselmann
        if server.using_ssl:
411 d859a2cf Michael Hanselmann
          sock.set_accept_state()
412 f2e13d55 Michael Hanselmann
          try:
413 d859a2cf Michael Hanselmann
            http.Handshake(sock, self.WRITE_TIMEOUT)
414 f2e13d55 Michael Hanselmann
          except http.HttpSessionHandshakeUnexpectedEOF:
415 f2e13d55 Michael Hanselmann
            # Ignore rest
416 f2e13d55 Michael Hanselmann
            return
417 f2e13d55 Michael Hanselmann
418 d859a2cf Michael Hanselmann
        (request_msg, request_msg_reader, force_close, response_msg) = \
419 d859a2cf Michael Hanselmann
          responder(compat.partial(self._ReadRequest, sock, self.READ_TIMEOUT))
420 d859a2cf Michael Hanselmann
        if response_msg:
421 d859a2cf Michael Hanselmann
          # HttpMessage.start_line can be of different types
422 57a6042e Guido Trotter
          # Instance of 'HttpClientToServerStartLine' has no 'code' member
423 57a6042e Guido Trotter
          # pylint: disable=E1103,E1101
424 d859a2cf Michael Hanselmann
          logging.info("%s:%s %s %s", client_addr[0], client_addr[1],
425 d859a2cf Michael Hanselmann
                       request_msg.start_line, response_msg.start_line.code)
426 d859a2cf Michael Hanselmann
          self._SendResponse(sock, request_msg, response_msg,
427 d859a2cf Michael Hanselmann
                             self.WRITE_TIMEOUT)
428 02cab3e7 Michael Hanselmann
      finally:
429 aea0ed67 Michael Hanselmann
        http.ShutdownConnection(sock, self.CLOSE_TIMEOUT, self.WRITE_TIMEOUT,
430 02cab3e7 Michael Hanselmann
                                request_msg_reader, force_close)
431 02cab3e7 Michael Hanselmann
432 d859a2cf Michael Hanselmann
      sock.close()
433 02cab3e7 Michael Hanselmann
    finally:
434 14d57a8b Iustin Pop
      logging.debug("Disconnected %s:%s", client_addr[0], client_addr[1])
435 02cab3e7 Michael Hanselmann
436 d859a2cf Michael Hanselmann
  @staticmethod
437 d859a2cf Michael Hanselmann
  def _ReadRequest(sock, timeout):
438 02cab3e7 Michael Hanselmann
    """Reads a request sent by client.
439 02cab3e7 Michael Hanselmann

440 02cab3e7 Michael Hanselmann
    """
441 d859a2cf Michael Hanselmann
    msg = http.HttpMessage()
442 d859a2cf Michael Hanselmann
443 02cab3e7 Michael Hanselmann
    try:
444 d859a2cf Michael Hanselmann
      reader = _HttpClientToServerMessageReader(sock, msg, timeout)
445 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
446 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while reading request")
447 02cab3e7 Michael Hanselmann
    except socket.error, err:
448 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error reading request: %s" % err)
449 02cab3e7 Michael Hanselmann
450 d859a2cf Michael Hanselmann
    return (msg, reader)
451 02cab3e7 Michael Hanselmann
452 d859a2cf Michael Hanselmann
  @staticmethod
453 d859a2cf Michael Hanselmann
  def _SendResponse(sock, req_msg, msg, timeout):
454 02cab3e7 Michael Hanselmann
    """Sends the response to the client.
455 02cab3e7 Michael Hanselmann

456 02cab3e7 Michael Hanselmann
    """
457 02cab3e7 Michael Hanselmann
    try:
458 d859a2cf Michael Hanselmann
      _HttpServerToClientMessageWriter(sock, req_msg, msg, timeout)
459 02cab3e7 Michael Hanselmann
    except http.HttpSocketTimeout:
460 02cab3e7 Michael Hanselmann
      raise http.HttpError("Timeout while sending response")
461 02cab3e7 Michael Hanselmann
    except socket.error, err:
462 02cab3e7 Michael Hanselmann
      raise http.HttpError("Error sending response: %s" % err)
463 02cab3e7 Michael Hanselmann
464 57fd6d0b Michael Hanselmann
465 112d240d Guido Trotter
class HttpServer(http.HttpBase, asyncore.dispatcher):
466 02cab3e7 Michael Hanselmann
  """Generic HTTP server class
467 02cab3e7 Michael Hanselmann

468 02cab3e7 Michael Hanselmann
  """
469 02cab3e7 Michael Hanselmann
  MAX_CHILDREN = 20
470 02cab3e7 Michael Hanselmann
471 e0003509 Michael Hanselmann
  def __init__(self, mainloop, local_address, port, handler,
472 1f8588f6 Iustin Pop
               ssl_params=None, ssl_verify_peer=False,
473 1f8588f6 Iustin Pop
               request_executor_class=None):
474 02cab3e7 Michael Hanselmann
    """Initializes the HTTP server
475 02cab3e7 Michael Hanselmann

476 02cab3e7 Michael Hanselmann
    @type mainloop: ganeti.daemon.Mainloop
477 02cab3e7 Michael Hanselmann
    @param mainloop: Mainloop used to poll for I/O events
478 c41eea6e Iustin Pop
    @type local_address: string
479 02cab3e7 Michael Hanselmann
    @param local_address: Local IP address to bind to
480 02cab3e7 Michael Hanselmann
    @type port: int
481 02cab3e7 Michael Hanselmann
    @param port: TCP port to listen on
482 02cab3e7 Michael Hanselmann
    @type ssl_params: HttpSslParams
483 02cab3e7 Michael Hanselmann
    @param ssl_params: SSL key and certificate
484 02cab3e7 Michael Hanselmann
    @type ssl_verify_peer: bool
485 25e7b43f Iustin Pop
    @param ssl_verify_peer: Whether to require client certificate
486 25e7b43f Iustin Pop
        and compare it with our certificate
487 1f8588f6 Iustin Pop
    @type request_executor_class: class
488 1f8588f6 Iustin Pop
    @param request_executor_class: an class derived from the
489 1f8588f6 Iustin Pop
        HttpServerRequestExecutor class
490 02cab3e7 Michael Hanselmann

491 02cab3e7 Michael Hanselmann
    """
492 f4322a1e Michael Hanselmann
    http.HttpBase.__init__(self)
493 112d240d Guido Trotter
    asyncore.dispatcher.__init__(self)
494 02cab3e7 Michael Hanselmann
495 1f8588f6 Iustin Pop
    if request_executor_class is None:
496 1f8588f6 Iustin Pop
      self.request_executor = HttpServerRequestExecutor
497 1f8588f6 Iustin Pop
    else:
498 1f8588f6 Iustin Pop
      self.request_executor = request_executor_class
499 1f8588f6 Iustin Pop
500 02cab3e7 Michael Hanselmann
    self.mainloop = mainloop
501 02cab3e7 Michael Hanselmann
    self.local_address = local_address
502 02cab3e7 Michael Hanselmann
    self.port = port
503 e0003509 Michael Hanselmann
    self.handler = handler
504 db4e138b Manuel Franceschini
    family = netutils.IPAddress.GetAddressFamily(local_address)
505 db4e138b Manuel Franceschini
    self.socket = self._CreateSocket(ssl_params, ssl_verify_peer, family)
506 02cab3e7 Michael Hanselmann
507 02cab3e7 Michael Hanselmann
    # Allow port to be reused
508 02cab3e7 Michael Hanselmann
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
509 02cab3e7 Michael Hanselmann
510 02cab3e7 Michael Hanselmann
    self._children = []
511 112d240d Guido Trotter
    self.set_socket(self.socket)
512 112d240d Guido Trotter
    self.accepting = True
513 02cab3e7 Michael Hanselmann
    mainloop.RegisterSignal(self)
514 02cab3e7 Michael Hanselmann
515 02cab3e7 Michael Hanselmann
  def Start(self):
516 02cab3e7 Michael Hanselmann
    self.socket.bind((self.local_address, self.port))
517 59305197 Michael Hanselmann
    self.socket.listen(1024)
518 02cab3e7 Michael Hanselmann
519 02cab3e7 Michael Hanselmann
  def Stop(self):
520 02cab3e7 Michael Hanselmann
    self.socket.close()
521 02cab3e7 Michael Hanselmann
522 112d240d Guido Trotter
  def handle_accept(self):
523 112d240d Guido Trotter
    self._IncomingConnection()
524 02cab3e7 Michael Hanselmann
525 02cab3e7 Michael Hanselmann
  def OnSignal(self, signum):
526 02cab3e7 Michael Hanselmann
    if signum == signal.SIGCHLD:
527 02cab3e7 Michael Hanselmann
      self._CollectChildren(True)
528 02cab3e7 Michael Hanselmann
529 02cab3e7 Michael Hanselmann
  def _CollectChildren(self, quick):
530 02cab3e7 Michael Hanselmann
    """Checks whether any child processes are done
531 02cab3e7 Michael Hanselmann

532 02cab3e7 Michael Hanselmann
    @type quick: bool
533 02cab3e7 Michael Hanselmann
    @param quick: Whether to only use non-blocking functions
534 02cab3e7 Michael Hanselmann

535 02cab3e7 Michael Hanselmann
    """
536 02cab3e7 Michael Hanselmann
    if not quick:
537 02cab3e7 Michael Hanselmann
      # Don't wait for other processes if it should be a quick check
538 02cab3e7 Michael Hanselmann
      while len(self._children) > self.MAX_CHILDREN:
539 02cab3e7 Michael Hanselmann
        try:
540 02cab3e7 Michael Hanselmann
          # Waiting without a timeout brings us into a potential DoS situation.
541 02cab3e7 Michael Hanselmann
          # As soon as too many children run, we'll not respond to new
542 02cab3e7 Michael Hanselmann
          # requests. The real solution would be to add a timeout for children
543 02cab3e7 Michael Hanselmann
          # and killing them after some time.
544 7c4d6c7b Michael Hanselmann
          pid, _ = os.waitpid(0, 0)
545 02cab3e7 Michael Hanselmann
        except os.error:
546 02cab3e7 Michael Hanselmann
          pid = None
547 02cab3e7 Michael Hanselmann
        if pid and pid in self._children:
548 02cab3e7 Michael Hanselmann
          self._children.remove(pid)
549 02cab3e7 Michael Hanselmann
550 02cab3e7 Michael Hanselmann
    for child in self._children:
551 02cab3e7 Michael Hanselmann
      try:
552 1122eb25 Iustin Pop
        pid, _ = os.waitpid(child, os.WNOHANG)
553 02cab3e7 Michael Hanselmann
      except os.error:
554 02cab3e7 Michael Hanselmann
        pid = None
555 02cab3e7 Michael Hanselmann
      if pid and pid in self._children:
556 02cab3e7 Michael Hanselmann
        self._children.remove(pid)
557 02cab3e7 Michael Hanselmann
558 02cab3e7 Michael Hanselmann
  def _IncomingConnection(self):
559 02cab3e7 Michael Hanselmann
    """Called for each incoming connection
560 02cab3e7 Michael Hanselmann

561 02cab3e7 Michael Hanselmann
    """
562 b459a848 Andrea Spadaccini
    # pylint: disable=W0212
563 02cab3e7 Michael Hanselmann
    (connection, client_addr) = self.socket.accept()
564 02cab3e7 Michael Hanselmann
565 02cab3e7 Michael Hanselmann
    self._CollectChildren(False)
566 02cab3e7 Michael Hanselmann
567 02cab3e7 Michael Hanselmann
    pid = os.fork()
568 02cab3e7 Michael Hanselmann
    if pid == 0:
569 02cab3e7 Michael Hanselmann
      # Child process
570 02cab3e7 Michael Hanselmann
      try:
571 bcb1a39e Michael Hanselmann
        # The client shouldn't keep the listening socket open. If the parent
572 bcb1a39e Michael Hanselmann
        # process is restarted, it would fail when there's already something
573 bcb1a39e Michael Hanselmann
        # listening (in this case its own child from a previous run) on the
574 bcb1a39e Michael Hanselmann
        # same port.
575 bcb1a39e Michael Hanselmann
        try:
576 bcb1a39e Michael Hanselmann
          self.socket.close()
577 bcb1a39e Michael Hanselmann
        except socket.error:
578 bcb1a39e Michael Hanselmann
          pass
579 bcb1a39e Michael Hanselmann
        self.socket = None
580 bcb1a39e Michael Hanselmann
581 82869978 Michael Hanselmann
        # In case the handler code uses temporary files
582 82869978 Michael Hanselmann
        utils.ResetTempfileModule()
583 82869978 Michael Hanselmann
584 e0003509 Michael Hanselmann
        self.request_executor(self, self.handler, connection, client_addr)
585 b459a848 Andrea Spadaccini
      except Exception: # pylint: disable=W0703
586 02cab3e7 Michael Hanselmann
        logging.exception("Error while handling request from %s:%s",
587 02cab3e7 Michael Hanselmann
                          client_addr[0], client_addr[1])
588 02cab3e7 Michael Hanselmann
        os._exit(1)
589 02cab3e7 Michael Hanselmann
      os._exit(0)
590 02cab3e7 Michael Hanselmann
    else:
591 02cab3e7 Michael Hanselmann
      self._children.append(pid)
592 02cab3e7 Michael Hanselmann
593 e0003509 Michael Hanselmann
594 e0003509 Michael Hanselmann
class HttpServerHandler(object):
595 e0003509 Michael Hanselmann
  """Base class for handling HTTP server requests.
596 e0003509 Michael Hanselmann

597 e0003509 Michael Hanselmann
  Users of this class must subclass it and override the L{HandleRequest}
598 e0003509 Michael Hanselmann
  function.
599 e0003509 Michael Hanselmann

600 e0003509 Michael Hanselmann
  """
601 f8bd7df3 Michael Hanselmann
  def PreHandleRequest(self, req):
602 f8bd7df3 Michael Hanselmann
    """Called before handling a request.
603 f8bd7df3 Michael Hanselmann

604 5bbd3f7f Michael Hanselmann
    Can be overridden by a subclass.
605 f8bd7df3 Michael Hanselmann

606 f8bd7df3 Michael Hanselmann
    """
607 f8bd7df3 Michael Hanselmann
608 02cab3e7 Michael Hanselmann
  def HandleRequest(self, req):
609 02cab3e7 Michael Hanselmann
    """Handles a request.
610 02cab3e7 Michael Hanselmann

611 5bbd3f7f Michael Hanselmann
    Must be overridden by subclass.
612 02cab3e7 Michael Hanselmann

613 02cab3e7 Michael Hanselmann
    """
614 02cab3e7 Michael Hanselmann
    raise NotImplementedError()
615 377ae13e Michael Hanselmann
616 377ae13e Michael Hanselmann
  @staticmethod
617 377ae13e Michael Hanselmann
  def FormatErrorMessage(values):
618 377ae13e Michael Hanselmann
    """Formats the body of an error message.
619 377ae13e Michael Hanselmann

620 377ae13e Michael Hanselmann
    @type values: dict
621 377ae13e Michael Hanselmann
    @param values: dictionary with keys C{code}, C{message} and C{explain}.
622 377ae13e Michael Hanselmann
    @rtype: tuple; (string, string)
623 377ae13e Michael Hanselmann
    @return: Content-type and response body
624 377ae13e Michael Hanselmann

625 377ae13e Michael Hanselmann
    """
626 377ae13e Michael Hanselmann
    return (DEFAULT_ERROR_CONTENT_TYPE, DEFAULT_ERROR_MESSAGE % values)